summaryrefslogtreecommitdiffstats
path: root/webapp
diff options
context:
space:
mode:
authorCorey Hulen <corey@hulen.com>2016-04-21 22:37:01 -0700
committerCorey Hulen <corey@hulen.com>2016-04-21 22:37:01 -0700
commit2e5617c29be69637acd384e85f795a0b343bec8d (patch)
tree6b8bdae1e664013b97c2dda94985375abda91aa5 /webapp
parent5c755463ed3a4c74a383fb4460b5be02d8868481 (diff)
downloadchat-2e5617c29be69637acd384e85f795a0b343bec8d.tar.gz
chat-2e5617c29be69637acd384e85f795a0b343bec8d.tar.bz2
chat-2e5617c29be69637acd384e85f795a0b343bec8d.zip
PLT-2057 User as a first class object (#2648)
* Adding TeamMember to system * Fixing all unit tests on the backend * Fixing merge conflicts * Fixing merge conflict * Adding javascript unit tests * Adding TeamMember to system * Fixing all unit tests on the backend * Fixing merge conflicts * Fixing merge conflict * Adding javascript unit tests * Adding client side unit test * Cleaning up the clint side tests * Fixing msg * Adding more client side unit tests * Adding more using tests * Adding last bit of client side unit tests and adding make cmd * Fixing bad merge * Fixing libraries * Updating to new client side API * Fixing borken unit test * Fixing unit tests * ugg...trying to beat gofmt * ugg...trying to beat gofmt * Cleaning up remainder of the server side routes * Adding inital load api * Increased coverage of webhook unit tests (#2660) * Adding loading ... to root html * Fixing bad merge * Removing explicit content type so superagent will guess corectly (#2685) * Fixing merge and unit tests * Adding create team UI * Fixing signup flows * Adding LDAP unit tests and enterprise unit test helper (#2702) * Add the ability to reset MFA from the commandline (#2706) * Fixing compliance unit tests * Fixing client side tests * Adding open server to system console * Moving websocket connection * Fixing unit test * Fixing unit tests * Fixing unit tests * Adding nickname and more LDAP unit tests (#2717) * Adding join open teams * Cleaning up all TODOs in the code * Fixing web sockets * Removing unused webockets file * PLT-2533 Add the ability to reset a user's MFA from the system console (#2715) * Add the ability to reset a user's MFA from the system console * Add client side unit test for adminResetMfa * Reorganizing authentication to fix LDAP error message (#2723) * Fixing failing unit test * Initial upgrade db code * Adding upgrade script * Fixing upgrade script after running on core * Update OAuth and Claim routes to work with user model changes (#2739) * Fixing perminant deletion. Adding ability to delete all user and the entire database (#2740) * Fixing team invite ldap login call (#2741) * Fixing bluebar and some img stuff * Fix all the different file upload web utils (#2743) * Fixing invalid session redirect (#2744) * Redirect on bad channel name (#2746) * Fixing a bunch of issue and removing dead code * Patch to fix error message on leave channel (#2747) * Setting EnableOpenServer to false by default * Fixing config * Fixing upgrade * Fixing reported bugs * Bug fixes for PLT-2057 * PLT-2563 Redo password recovery to use a database table (#2745) * Redo password recovery to use a database table * Update reset password audits * Split out admin and user reset password APIs to be separate * Delete password recovery when user is permanently deleted * Consolidate password resetting into a single function * Removed private channels as an option for outgoing webhooks (#2752) * PLT-2577/PLT-2552 Fixes for backstage (#2753) * Added URL to incoming webhook list * Fixed client functions for adding/removing integrations * Disallowed slash commands without trigger words * Fixed clientside handling of errors on AddCommand page * Minor auth cleanup (#2758) * Changed EditPostModal to just close if you save without making any changes (#2759) * Renamed client -> Client in async_client.jsx and fixed eslint warnings (#2756) * Fixed url in channel info modal (#2755) * Fixing reported issues * Moving to version 3 of the apis * Fixing command unit tests (#2760) * Adding team admins * Fixing DM issue * Fixing eslint error * Properly set EditPostModal's originalText state in all cases (#2762) * Update client config check to assume features is defined if server is licensed (#2772) * Fixing url link * Fixing issue with websocket crashing when sending messages to different teams
Diffstat (limited to 'webapp')
-rw-r--r--webapp/.eslintrc.json7
-rw-r--r--webapp/Makefile2
-rw-r--r--webapp/action_creators/global_actions.jsx113
-rw-r--r--webapp/action_creators/websocket_actions.jsx19
-rw-r--r--webapp/client/client.jsx1463
-rw-r--r--webapp/components/activity_log_modal.jsx2
-rw-r--r--webapp/components/admin_console/admin_navbar_dropdown.jsx20
-rw-r--r--webapp/components/admin_console/admin_sidebar_header.jsx3
-rw-r--r--webapp/components/admin_console/compliance_reports.jsx4
-rw-r--r--webapp/components/admin_console/compliance_settings.jsx2
-rw-r--r--webapp/components/admin_console/email_settings.jsx2
-rw-r--r--webapp/components/admin_console/gitlab_settings.jsx2
-rw-r--r--webapp/components/admin_console/image_settings.jsx2
-rw-r--r--webapp/components/admin_console/ldap_settings.jsx32
-rw-r--r--webapp/components/admin_console/legal_and_support_settings.jsx2
-rw-r--r--webapp/components/admin_console/license_settings.jsx7
-rw-r--r--webapp/components/admin_console/log_settings.jsx2
-rw-r--r--webapp/components/admin_console/privacy_settings.jsx2
-rw-r--r--webapp/components/admin_console/rate_settings.jsx2
-rw-r--r--webapp/components/admin_console/reset_password_modal.jsx13
-rw-r--r--webapp/components/admin_console/service_settings.jsx2
-rw-r--r--webapp/components/admin_console/sql_settings.jsx2
-rw-r--r--webapp/components/admin_console/team_settings.jsx105
-rw-r--r--webapp/components/admin_console/team_users.jsx2
-rw-r--r--webapp/components/admin_console/user_item.jsx133
-rw-r--r--webapp/components/authorize.jsx2
-rw-r--r--webapp/components/backstage/add_command.jsx10
-rw-r--r--webapp/components/backstage/add_incoming_webhook.jsx5
-rw-r--r--webapp/components/backstage/add_outgoing_webhook.jsx7
-rw-r--r--webapp/components/backstage/backstage_navbar.jsx2
-rw-r--r--webapp/components/backstage/backstage_sidebar.jsx3
-rw-r--r--webapp/components/backstage/installed_commands.jsx3
-rw-r--r--webapp/components/backstage/installed_incoming_webhook.jsx11
-rw-r--r--webapp/components/backstage/installed_incoming_webhooks.jsx3
-rw-r--r--webapp/components/backstage/installed_outgoing_webhooks.jsx3
-rw-r--r--webapp/components/backstage/integrations.jsx7
-rw-r--r--webapp/components/channel_header.jsx2
-rw-r--r--webapp/components/channel_info_modal.jsx30
-rw-r--r--webapp/components/channel_invite_button.jsx8
-rw-r--r--webapp/components/channel_members_modal.jsx7
-rw-r--r--webapp/components/channel_notifications_modal.jsx6
-rw-r--r--webapp/components/channel_select.jsx2
-rw-r--r--webapp/components/claim/claim.jsx32
-rw-r--r--webapp/components/claim/components/email_to_ldap.jsx47
-rw-r--r--webapp/components/claim/components/email_to_oauth.jsx23
-rw-r--r--webapp/components/claim/components/ldap_to_email.jsx33
-rw-r--r--webapp/components/claim/components/oauth_to_email.jsx18
-rw-r--r--webapp/components/create_comment.jsx3
-rw-r--r--webapp/components/create_post.jsx4
-rw-r--r--webapp/components/create_team/components/display_name.jsx (renamed from webapp/components/signup_team_complete/components/team_signup_display_name_page.jsx)33
-rw-r--r--webapp/components/create_team/components/team_url.jsx (renamed from webapp/components/signup_team_complete/components/team_signup_url_page.jsx)88
-rw-r--r--webapp/components/create_team/create_team.jsx72
-rw-r--r--webapp/components/delete_channel_modal.jsx2
-rw-r--r--webapp/components/delete_post_modal.jsx2
-rw-r--r--webapp/components/do_verify_email.jsx8
-rw-r--r--webapp/components/edit_channel_header_modal.jsx2
-rw-r--r--webapp/components/edit_channel_purpose_modal.jsx11
-rw-r--r--webapp/components/edit_post_modal.jsx49
-rw-r--r--webapp/components/error_bar.jsx4
-rw-r--r--webapp/components/file_attachment.jsx4
-rw-r--r--webapp/components/file_upload.jsx46
-rw-r--r--webapp/components/filtered_user_list.jsx3
-rw-r--r--webapp/components/header_footer_template.jsx (renamed from webapp/components/not_logged_in.jsx)8
-rw-r--r--webapp/components/invite_member_modal.jsx2
-rw-r--r--webapp/components/logged_in.jsx147
-rw-r--r--webapp/components/login/login.jsx198
-rw-r--r--webapp/components/member_list_team.jsx16
-rw-r--r--webapp/components/more_channels.jsx2
-rw-r--r--webapp/components/navbar.jsx2
-rw-r--r--webapp/components/navbar_dropdown.jsx59
-rw-r--r--webapp/components/needs_team.jsx148
-rw-r--r--webapp/components/new_channel_flow.jsx8
-rw-r--r--webapp/components/notify_counts.jsx11
-rw-r--r--webapp/components/password_reset_form.jsx14
-rw-r--r--webapp/components/password_reset_send_link.jsx8
-rw-r--r--webapp/components/popover_list_members.jsx3
-rw-r--r--webapp/components/post.jsx4
-rw-r--r--webapp/components/register_app_modal.jsx2
-rw-r--r--webapp/components/rename_channel_modal.jsx2
-rw-r--r--webapp/components/rhs_comment.jsx6
-rw-r--r--webapp/components/rhs_header_post.jsx12
-rw-r--r--webapp/components/root.jsx50
-rw-r--r--webapp/components/search_bar.jsx2
-rw-r--r--webapp/components/select_team/select_team.jsx257
-rw-r--r--webapp/components/should_verify_email.jsx20
-rw-r--r--webapp/components/sidebar_header.jsx3
-rw-r--r--webapp/components/sidebar_right_menu.jsx7
-rw-r--r--webapp/components/signup_team.jsx235
-rw-r--r--webapp/components/signup_team_complete/components/signup_team_complete.jsx1
-rw-r--r--webapp/components/signup_team_complete/components/team_signup_email_item.jsx89
-rw-r--r--webapp/components/signup_team_complete/components/team_signup_finished.jsx17
-rw-r--r--webapp/components/signup_team_complete/components/team_signup_password_page.jsx225
-rw-r--r--webapp/components/signup_team_complete/components/team_signup_send_invites_page.jsx215
-rw-r--r--webapp/components/signup_team_complete/components/team_signup_username_page.jsx169
-rw-r--r--webapp/components/signup_team_complete/components/team_signup_welcome_page.jsx243
-rw-r--r--webapp/components/signup_team_confirm.jsx49
-rw-r--r--webapp/components/signup_user_complete.jsx199
-rw-r--r--webapp/components/suggestion/at_mention_provider.jsx3
-rw-r--r--webapp/components/suggestion/search_user_provider.jsx3
-rw-r--r--webapp/components/team_export_tab.jsx2
-rw-r--r--webapp/components/team_general_tab.jsx160
-rw-r--r--webapp/components/team_members_dropdown.jsx46
-rw-r--r--webapp/components/team_settings.jsx1
-rw-r--r--webapp/components/team_signup_choose_auth.jsx132
-rw-r--r--webapp/components/team_signup_with_email.jsx116
-rw-r--r--webapp/components/team_signup_with_ldap.jsx231
-rw-r--r--webapp/components/team_signup_with_sso.jsx174
-rw-r--r--webapp/components/toggle_modal_button.jsx7
-rw-r--r--webapp/components/user_list.jsx10
-rw-r--r--webapp/components/user_list_row.jsx11
-rw-r--r--webapp/components/user_profile.jsx3
-rw-r--r--webapp/components/user_settings/import_theme_modal.jsx2
-rw-r--r--webapp/components/user_settings/manage_languages.jsx2
-rw-r--r--webapp/components/user_settings/user_settings_general.jsx8
-rw-r--r--webapp/components/user_settings/user_settings_notifications.jsx2
-rw-r--r--webapp/components/user_settings/user_settings_security.jsx37
-rw-r--r--webapp/components/user_settings/user_settings_theme.jsx2
-rw-r--r--webapp/components/view_image.jsx2
-rw-r--r--webapp/i18n/en.json104
-rw-r--r--webapp/i18n/es.json64
-rw-r--r--webapp/i18n/fr.json65
-rw-r--r--webapp/i18n/ja.json55
-rw-r--r--webapp/i18n/pt.json64
-rw-r--r--webapp/package.json17
-rw-r--r--webapp/root.html13
-rw-r--r--webapp/root.jsx463
-rw-r--r--webapp/sass/routes/_backstage.scss1
-rw-r--r--webapp/sass/routes/_signup.scss11
-rw-r--r--webapp/stores/browser_store.jsx12
-rw-r--r--webapp/stores/error_store.jsx11
-rw-r--r--webapp/stores/preference_store.jsx2
-rw-r--r--webapp/stores/team_store.jsx47
-rw-r--r--webapp/stores/user_store.jsx33
-rw-r--r--webapp/tests/client_channel.test.jsx334
-rw-r--r--webapp/tests/client_command.test.jsx123
-rw-r--r--webapp/tests/client_general.test.jsx333
-rw-r--r--webapp/tests/client_hooks.test.jsx139
-rw-r--r--webapp/tests/client_oauth.test.jsx60
-rw-r--r--webapp/tests/client_post.test.jsx207
-rw-r--r--webapp/tests/client_preferences.test.jsx72
-rw-r--r--webapp/tests/client_team.test.jsx235
-rw-r--r--webapp/tests/client_user.test.jsx550
-rw-r--r--webapp/tests/spinner_button.test.jsx24
-rw-r--r--webapp/tests/test_helper.jsx183
-rw-r--r--webapp/utils/async_client.jsx324
-rw-r--r--webapp/utils/channel_intro_messages.jsx3
-rw-r--r--webapp/utils/client.jsx1759
-rw-r--r--webapp/utils/constants.jsx5
-rw-r--r--webapp/utils/utils.jsx20
-rw-r--r--webapp/utils/web_client.jsx67
-rw-r--r--webapp/webpack.config-test.js131
-rw-r--r--webapp/webpack.config.js3
152 files changed, 6045 insertions, 5416 deletions
diff --git a/webapp/.eslintrc.json b/webapp/.eslintrc.json
index 13c9ee97f..3eb02cc40 100644
--- a/webapp/.eslintrc.json
+++ b/webapp/.eslintrc.json
@@ -19,6 +19,13 @@
"jquery": true,
"es6": true
},
+ "globals": {
+ "jest": true,
+ "describe": true,
+ "it": true,
+ "expect": true,
+ "before": true
+ },
"rules": {
"comma-dangle": [2, "never"],
"array-callback-return": 2,
diff --git a/webapp/Makefile b/webapp/Makefile
index 6ec75d1df..b0c2c831a 100644
--- a/webapp/Makefile
+++ b/webapp/Makefile
@@ -1,6 +1,6 @@
.PHONY: build test run clean stop
-test:
+test: .npminstall
@echo Checking for style guide compliance
npm run check
diff --git a/webapp/action_creators/global_actions.jsx b/webapp/action_creators/global_actions.jsx
index fd447ec93..5641246f9 100644
--- a/webapp/action_creators/global_actions.jsx
+++ b/webapp/action_creators/global_actions.jsx
@@ -4,11 +4,16 @@
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import PostStore from 'stores/post_store.jsx';
+import UserStore from 'stores/user_store.jsx';
+import BrowserStore from 'stores/browser_store.jsx';
+import ErrorStore from 'stores/error_store.jsx';
+import TeamStore from 'stores/team_store.jsx';
+import PreferenceStore from 'stores/preference_store.jsx';
import SearchStore from 'stores/search_store.jsx';
import Constants from 'utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
import * as AsyncClient from 'utils/async_client.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as Utils from 'utils/utils.jsx';
import * as Websockets from './websocket_actions.jsx';
import * as I18n from 'i18n/i18n.jsx';
@@ -21,7 +26,6 @@ export function emitChannelClickEvent(channel) {
function userVisitedFakeChannel(chan, success, fail) {
const otherUserId = Utils.getUserIdFromChannelName(chan);
Client.createDirectChannel(
- chan,
otherUserId,
(data) => {
success(data);
@@ -61,6 +65,69 @@ export function emitChannelClickEvent(channel) {
}
}
+export function emitInitialLoad(callback) {
+ Client.getInitialLoad(
+ (data) => {
+ global.window.mm_config = data.client_cfg;
+ global.window.mm_license = data.license_cfg;
+
+ UserStore.setNoAccounts(data.no_accounts);
+
+ if (data.user && data.user.id) {
+ global.window.mm_user = data.user;
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_ME,
+ me: data.user
+ });
+ }
+
+ if (data.preferences) {
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_PREFERENCES,
+ preferences: data.preferences
+ });
+ }
+
+ if (data.teams) {
+ var teams = {};
+ data.teams.forEach((team) => {
+ teams[team.id] = team;
+ });
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_ALL_TEAMS,
+ teams
+ });
+ }
+
+ if (data.team_members) {
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_TEAM_MEMBERS,
+ team_members: data.team_members
+ });
+ }
+
+ if (data.direct_profiles) {
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_DIRECT_PROFILES,
+ profiles: data.direct_profiles
+ });
+ }
+
+ if (callback) {
+ callback();
+ }
+ },
+ (err) => {
+ AsyncClient.dispatchError(err, 'getInitialLoad');
+
+ if (callback) {
+ callback();
+ }
+ }
+ );
+}
+
export function emitPostFocusEvent(postId) {
AsyncClient.getChannels(true);
Client.getPostById(
@@ -80,6 +147,18 @@ export function emitPostFocusEvent(postId) {
);
}
+export function emitCloseRightHandSide() {
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_SEARCH,
+ results: null
+ });
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_POST_SELECTED,
+ postId: null
+ });
+}
+
export function emitPostFocusRightHandSideFromSearch(post, isMentionSearch) {
Client.getPost(
post.channel_id,
@@ -133,21 +212,21 @@ export function emitLoadMorePostsFocusedBottomEvent() {
AsyncClient.getPostsAfter(latestPostId, 0, Constants.POST_CHUNK_SIZE);
}
-export function emitPostRecievedEvent(post, websocketMessageProps) {
+export function emitPostRecievedEvent(post, msg) {
if (ChannelStore.getCurrentId() === post.channel_id) {
if (window.isActive) {
AsyncClient.updateLastViewedAt();
} else {
AsyncClient.getChannel(post.channel_id);
}
- } else {
+ } else if (msg && TeamStore.getCurrentId() === msg.team_id) {
AsyncClient.getChannel(post.channel_id);
}
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_POST,
post,
- websocketMessageProps
+ websocketMessageProps: msg.props
});
}
@@ -271,10 +350,6 @@ export function sendEphemeralPost(message, channelId) {
emitPostRecievedEvent(post);
}
-export function loadTeamRequiredPage() {
- AsyncClient.getAllTeams();
-}
-
export function newLocalizationSelected(locale) {
if (locale === 'en') {
AppDispatcher.handleServerAction({
@@ -311,8 +386,6 @@ export function loadBrowserLocale() {
export function viewLoggedIn() {
AsyncClient.getChannels();
AsyncClient.getChannelExtraInfo();
- AsyncClient.getMyTeam();
- AsyncClient.getMe();
// Clear pending posts (shouldn't have pending posts if we are loading)
PostStore.clearPendingPosts();
@@ -335,3 +408,21 @@ export function emitRemoteUserTypingEvent(channelId, userId, postParentId) {
postParentId
});
}
+
+export function emitUserLoggedOutEvent(redirectTo) {
+ const rURL = (redirectTo && typeof redirectTo === 'string') ? redirectTo : '/';
+ Client.logout(
+ () => {
+ BrowserStore.signalLogout();
+ BrowserStore.clear();
+ ErrorStore.clearLastError();
+ PreferenceStore.clear();
+ UserStore.clear();
+ TeamStore.clear();
+ browserHistory.push(rURL);
+ },
+ () => {
+ browserHistory.push(rURL);
+ }
+ );
+}
diff --git a/webapp/action_creators/websocket_actions.jsx b/webapp/action_creators/websocket_actions.jsx
index a66d79d18..c4e9c63c2 100644
--- a/webapp/action_creators/websocket_actions.jsx
+++ b/webapp/action_creators/websocket_actions.jsx
@@ -3,12 +3,14 @@
import $ from 'jquery';
import UserStore from 'stores/user_store.jsx';
+import TeamStore from 'stores/team_store.jsx';
import PostStore from 'stores/post_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import BrowserStore from 'stores/browser_store.jsx';
import ErrorStore from 'stores/error_store.jsx';
import NotificationStore from 'stores/notification_store.jsx'; //eslint-disable-line no-unused-vars
+import Client from 'utils/web_client.jsx';
import * as Utils from 'utils/utils.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
@@ -31,7 +33,7 @@ export function initialize() {
protocol = 'wss://';
}
- const connUrl = protocol + location.host + ((/:\d+/).test(location.host) ? '' : Utils.getWebsocketPort(protocol)) + '/api/v1/websocket';
+ const connUrl = protocol + location.host + ((/:\d+/).test(location.host) ? '' : Utils.getWebsocketPort(protocol)) + Client.getUsersRoute() + '/websocket';
if (connectFailCount === 0) {
console.log('websocket connecting to ' + connUrl); //eslint-disable-line no-console
@@ -145,6 +147,11 @@ function handleMessage(msg) {
export function sendMessage(msg) {
if (conn && conn.readyState === WebSocket.OPEN) {
+ var teamId = TeamStore.getCurrentId();
+ if (teamId && teamId.length > 0) {
+ msg.team_id = teamId;
+ }
+
conn.send(JSON.stringify(msg));
} else if (!conn || conn.readyState === WebSocket.Closed) {
conn = null;
@@ -161,7 +168,7 @@ export function close() {
function handleNewPostEvent(msg) {
const post = JSON.parse(msg.props.post);
- GlobalActions.emitPostRecievedEvent(post, msg.props);
+ GlobalActions.emitPostRecievedEvent(post, msg);
}
function handlePostEditEvent(msg) {
@@ -193,7 +200,7 @@ function handleUserAddedEvent(msg) {
AsyncClient.getChannelExtraInfo();
}
- if (UserStore.getCurrentId() === msg.user_id) {
+ if (TeamStore.getCurrentId() === msg.team_id && UserStore.getCurrentId() === msg.user_id) {
AsyncClient.getChannel(msg.channel_id);
}
}
@@ -219,7 +226,7 @@ function handleUserRemovedEvent(msg) {
function handleChannelViewedEvent(msg) {
// Useful for when multiple devices have the app open to different channels
- if (ChannelStore.getCurrentId() !== msg.channel_id && UserStore.getCurrentId() === msg.user_id) {
+ if (TeamStore.getCurrentId() === msg.team_id && ChannelStore.getCurrentId() !== msg.channel_id && UserStore.getCurrentId() === msg.user_id) {
AsyncClient.getChannel(msg.channel_id);
}
}
@@ -230,5 +237,7 @@ function handlePreferenceChangedEvent(msg) {
}
function handleUserTypingEvent(msg) {
- GlobalActions.emitRemoteUserTypingEvent(msg.channel_id, msg.user_id, msg.props.parent_id);
+ if (TeamStore.getCurrentId() === msg.team_id) {
+ GlobalActions.emitRemoteUserTypingEvent(msg.channel_id, msg.user_id, msg.props.parent_id);
+ }
}
diff --git a/webapp/client/client.jsx b/webapp/client/client.jsx
new file mode 100644
index 000000000..98e660227
--- /dev/null
+++ b/webapp/client/client.jsx
@@ -0,0 +1,1463 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import request from 'superagent';
+
+const HEADER_X_VERSION_ID = 'x-version-id';
+const HEADER_TOKEN = 'token';
+const HEADER_BEARER = 'BEARER';
+const HEADER_AUTH = 'Authorization';
+
+export default class Client {
+ constructor() {
+ this.teamId = '';
+ this.serverVersion = '';
+ this.logToConsole = false;
+ this.useToken = false;
+ this.token = '';
+ this.url = '';
+ this.urlVersion = '/api/v3';
+ this.defaultHeaders = {
+ 'X-Requested-With': 'XMLHttpRequest'
+ };
+
+ this.translations = {
+ connectionError: 'There appears to be a problem with your internet connection.',
+ unknownError: 'We received an unexpected status code from the server.'
+ };
+ }
+
+ setUrl = (url) => {
+ this.url = url;
+ }
+
+ setTeamId = (id) => {
+ this.teamId = id;
+ }
+
+ getTeamId = () => {
+ if (this.teamId === '') {
+ console.error('You are trying to use a route that requires a team_id, but you have not called setTeamId() in client.jsx'); // eslint-disable-line no-console
+ }
+
+ return this.teamId;
+ }
+
+ getServerVersion = () => {
+ return this.serverVersion;
+ }
+
+ getBaseRoute() {
+ return `${this.url}${this.urlVersion}`;
+ }
+
+ getAdminRoute() {
+ return `${this.url}${this.urlVersion}/admin`;
+ }
+
+ getLicenseRoute() {
+ return `${this.url}${this.urlVersion}/license`;
+ }
+
+ getTeamsRoute() {
+ return `${this.url}${this.urlVersion}/teams`;
+ }
+
+ getTeamNeededRoute() {
+ return `${this.url}${this.urlVersion}/teams/${this.getTeamId()}`;
+ }
+
+ getChannelsRoute() {
+ return `${this.url}${this.urlVersion}/teams/${this.getTeamId()}/channels`;
+ }
+
+ getChannelNeededRoute(channelId) {
+ return `${this.url}${this.urlVersion}/teams/${this.getTeamId()}/channels/${channelId}`;
+ }
+
+ getCommandsRoute() {
+ return `${this.url}${this.urlVersion}/teams/${this.getTeamId()}/commands`;
+ }
+
+ getHooksRoute() {
+ return `${this.url}${this.urlVersion}/teams/${this.getTeamId()}/hooks`;
+ }
+
+ getPostsRoute(channelId) {
+ return `${this.url}${this.urlVersion}/teams/${this.getTeamId()}/channels/${channelId}/posts`;
+ }
+
+ getUsersRoute() {
+ return `${this.url}${this.urlVersion}/users`;
+ }
+
+ getFilesRoute() {
+ return `${this.url}${this.urlVersion}/teams/${this.getTeamId()}/files`;
+ }
+
+ getOAuthRoute() {
+ return `${this.url}${this.urlVersion}/oauth`;
+ }
+
+ getUserNeededRoute(userId) {
+ return `${this.url}${this.urlVersion}/users/${userId}`;
+ }
+
+ setTranslations = (messages) => {
+ this.translations = messages;
+ }
+
+ enableLogErrorsToConsole = (enabled) => {
+ this.logToConsole = enabled;
+ }
+
+ useHeaderToken = () => {
+ this.useToken = true;
+ if (this.token !== '') {
+ this.defaultHeaders[HEADER_AUTH] = `${HEADER_BEARER} ${this.token}`;
+ }
+ }
+
+ track = (category, action, label, property, value) => { // eslint-disable-line no-unused-vars
+ // NO-OP for inherited classes to override
+ }
+
+ trackPage = () => {
+ // NO-OP for inherited classes to override
+ }
+
+ handleError = (err, res) => { // eslint-disable-line no-unused-vars
+ // NO-OP for inherited classes to override
+ }
+
+ handleResponse = (methodName, successCallback, errorCallback, err, res) => {
+ if (res && res.header) {
+ this.serverVersion = res.header[HEADER_X_VERSION_ID];
+ if (res.header[HEADER_X_VERSION_ID]) {
+ this.serverVersion = res.header[HEADER_X_VERSION_ID];
+ }
+ }
+
+ if (err) {
+ // test to make sure it looks like a server JSON error response
+ var e = null;
+ if (res && res.body && res.body.id) {
+ e = res.body;
+ }
+
+ var msg = '';
+
+ if (e) {
+ msg = 'method=' + methodName + ' msg=' + e.message + ' detail=' + e.detailed_error + ' rid=' + e.request_id;
+ } else {
+ msg = 'method=' + methodName + ' status=' + err.status + ' statusCode=' + err.statusCode + ' err=' + err;
+
+ if (err.status === 0 || !err.status) {
+ e = {message: this.translations.connectionError};
+ } else {
+ e = {message: this.translations.unknownError + ' (' + err.status + ')'};
+ }
+ }
+
+ if (this.logToConsole) {
+ console.error(msg); // eslint-disable-line no-console
+ console.error(e); // eslint-disable-line no-console
+ }
+
+ this.track('api', 'api_weberror', methodName, 'message', msg);
+
+ this.handleError(err, res);
+
+ if (errorCallback) {
+ errorCallback(e, err, res);
+ return;
+ }
+ }
+
+ if (successCallback) {
+ successCallback(res.body, res);
+ }
+ }
+
+ // General / Admin / Licensing Routes Section
+
+ getTranslations = (url, success, error) => {
+ return request.
+ get(url).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getTranslations', success, error));
+ }
+
+ getClientConfig = (success, error) => {
+ return request.
+ get(`${this.getAdminRoute()}/client_props`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getClientConfig', success, error));
+ }
+
+ getComplianceReports = (success, error) => {
+ return request.
+ get(`${this.getAdminRoute()}/compliance_reports`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getComplianceReports', success, error));
+ }
+
+ uploadBrandImage = (image, success, error) => {
+ request.
+ post(`${this.getAdminRoute()}/upload_brand_image`).
+ set(this.defaultHeaders).
+ accept('application/json').
+ attach('image', image, image.name).
+ end(this.handleResponse.bind(this, 'uploadBrandImage', success, error));
+ }
+
+ saveComplianceReports = (job, success, error) => {
+ return request.
+ post(`${this.getAdminRoute()}/save_compliance_report`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(job).
+ end(this.handleResponse.bind(this, 'saveComplianceReports', success, error));
+ }
+
+ getLogs = (success, error) => {
+ return request.
+ get(`${this.getAdminRoute()}/logs`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getLogs', success, error));
+ }
+
+ getServerAudits = (success, error) => {
+ return request.
+ get(`${this.getAdminRoute()}/audits`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getServerAudits', success, error));
+ }
+
+ getConfig = (success, error) => {
+ return request.
+ get(`${this.getAdminRoute()}/config`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getConfig', success, error));
+ }
+
+ getAnalytics = (name, teamId, success, error) => {
+ let url = `${this.getAdminRoute()}/analytics/`;
+ if (teamId == null) {
+ url += name;
+ } else {
+ url += teamId + '/' + name;
+ }
+
+ return request.
+ get(url).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getAnalytics', success, error));
+ }
+
+ getTeamAnalytics = (teamId, name, success, error) => {
+ return request.
+ get(`${this.getAdminRoute()}/analytics/${teamId}/${name}`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getTeamAnalytics', success, error));
+ }
+
+ saveConfig = (config, success, error) => {
+ request.
+ post(`${this.getAdminRoute()}/save_config`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(config).
+ end(this.handleResponse.bind(this, 'saveConfig', success, error));
+ }
+
+ testEmail = (config, success, error) => {
+ request.
+ post(`${this.getAdminRoute()}/test_email`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(config).
+ end(this.handleResponse.bind(this, 'testEmail', success, error));
+ }
+
+ logClientError = (msg) => {
+ var l = {};
+ l.level = 'ERROR';
+ l.message = msg;
+
+ request.
+ post(`${this.getAdminRoute()}/log_client`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(l).
+ end(this.handleResponse.bind(this, 'logClientError', null, null));
+ }
+
+ getClientLicenceConfig = (success, error) => {
+ request.
+ get(`${this.getLicenseRoute()}/client_config`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getClientLicenceConfig', success, error));
+ }
+
+ removeLicenseFile = (success, error) => {
+ request.
+ post(`${this.getLicenseRoute()}/remove`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'removeLicenseFile', success, error));
+ }
+
+ uploadLicenseFile = (license, success, error) => {
+ request.
+ post(`${this.getLicenseRoute()}/add`).
+ set(this.defaultHeaders).
+ accept('application/json').
+ attach('license', license, license.name).
+ end(this.handleResponse.bind(this, 'uploadLicenseFile', success, error));
+
+ this.track('api', 'api_license_upload');
+ }
+
+ importSlack = (fileData, success, error) => {
+ request.
+ post(`${this.getTeamsRoute()}/import_team`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(fileData).
+ end(this.handleResponse.bind(this, 'importSlack', success, error));
+ }
+
+ exportTeam = (success, error) => {
+ request.
+ get(`${this.getTeamsRoute()}/export_team`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'exportTeam', success, error));
+ }
+
+ signupTeam = (email, success, error) => {
+ request.
+ post(`${this.getTeamsRoute()}/signup`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({email}).
+ end(this.handleResponse.bind(this, 'signupTeam', success, error));
+
+ this.track('api', 'api_teams_signup');
+ }
+
+ adminResetMfa = (userId, success, error) => {
+ const data = {};
+ data.user_id = userId;
+
+ request.
+ post(`${this.getAdminRoute()}/reset_mfa`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'adminResetMfa', success, error));
+ }
+
+ adminResetPassword = (userId, newPassword, success, error) => {
+ var data = {};
+ data.new_password = newPassword;
+ data.user_id = userId;
+
+ request.
+ post(`${this.getAdminRoute()}/reset_password`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'adminResetPassword', success, error));
+
+ this.track('api', 'api_admin_reset_password');
+ }
+
+ // Team Routes Section
+
+ createTeamFromSignup = (teamSignup, success, error) => {
+ request.
+ post(`${this.getTeamsRoute()}/create_from_signup`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(teamSignup).
+ end(this.handleResponse.bind(this, 'createTeamFromSignup', success, error));
+ }
+
+ findTeamByName = (teamName, success, error) => {
+ request.
+ post(`${this.getTeamsRoute()}/find_team_by_name`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({name: teamName}).
+ end(this.handleResponse.bind(this, 'findTeamByName', success, error));
+ }
+
+ createTeam = (team, success, error) => {
+ request.
+ post(`${this.getTeamsRoute()}/create`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(team).
+ end(this.handleResponse.bind(this, 'createTeam', success, error));
+
+ this.track('api', 'api_users_create', '', 'email', team.name);
+ }
+
+ updateTeam = (team, success, error) => {
+ request.
+ post(`${this.getTeamNeededRoute()}/update`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(team).
+ end(this.handleResponse.bind(this, 'updateTeam', success, error));
+
+ this.track('api', 'api_teams_update_name');
+ }
+
+ getAllTeams = (success, error) => {
+ request.
+ get(`${this.getTeamsRoute()}/all`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getAllTeams', success, error));
+ }
+
+ getAllTeamListings = (success, error) => {
+ request.
+ get(`${this.getTeamsRoute()}/all_team_listings`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getAllTeamListings', success, error));
+ }
+
+ getMyTeam = (success, error) => {
+ request.
+ get(`${this.getTeamNeededRoute()}/me`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getMyTeam', success, error));
+ }
+
+ getTeamMembers = (teamId, success, error) => {
+ request.
+ get(`${this.getTeamsRoute()}/members/${teamId}`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getTeamMembers', success, error));
+ }
+
+ inviteMembers = (data, success, error) => {
+ request.
+ post(`${this.getTeamNeededRoute()}/invite_members`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'inviteMembers', success, error));
+
+ this.track('api', 'api_teams_invite_members');
+ }
+
+ addUserToTeam = (userId, success, error) => {
+ request.
+ post(`${this.getTeamNeededRoute()}/add_user_to_team`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({user_id: userId}).
+ end(this.handleResponse.bind(this, 'addUserToTeam', success, error));
+
+ this.track('api', 'api_teams_invite_members');
+ }
+
+ addUserToTeamFromInvite = (data, hash, inviteId, success, error) => {
+ request.
+ post(`${this.getTeamsRoute()}/add_user_to_team_from_invite`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({hash, data, invite_id: inviteId}).
+ end(this.handleResponse.bind(this, 'addUserToTeam', success, error));
+
+ this.track('api', 'api_teams_invite_members');
+ }
+
+ getInviteInfo = (inviteId, success, error) => {
+ request.
+ post(`${this.getTeamsRoute()}/get_invite_info`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({invite_id: inviteId}).
+ end(this.handleResponse.bind(this, 'getInviteInfo', success, error));
+ }
+
+ // User Routes Setions
+
+ createUser = (user, success, error) => {
+ this.createUserWithInvite(user, null, null, null, success, error);
+ }
+
+ createUserWithInvite = (user, data, emailHash, inviteId, success, error) => {
+ var url = `${this.getUsersRoute()}/create`;
+
+ if (data || emailHash || inviteId) {
+ url += '?d=' + encodeURIComponent(data) + '&h=' + encodeURIComponent(emailHash) + '&iid=' + encodeURIComponent(inviteId);
+ }
+
+ request.
+ post(url).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(user).
+ end(this.handleResponse.bind(this, 'createUser', success, error));
+
+ this.track('api', 'api_users_create', '', 'email', user.email);
+ }
+
+ updateUser = (user, success, error) => {
+ request.
+ post(`${this.getUsersRoute()}/update`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(user).
+ end(this.handleResponse.bind(this, 'updateUser', success, error));
+
+ this.track('api', 'api_users_update');
+ }
+
+ updatePassword = (userId, currentPassword, newPassword, success, error) => {
+ var data = {};
+ data.user_id = userId;
+ data.current_password = currentPassword;
+ data.new_password = newPassword;
+
+ request.
+ post(`${this.getUsersRoute()}/newpassword`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'updatePassword', success, error));
+
+ this.track('api', 'api_users_newpassword');
+ }
+
+ updateUserNotifyProps = (notifyProps, success, error) => {
+ request.
+ post(`${this.getUsersRoute()}/update_notify`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(notifyProps).
+ end(this.handleResponse.bind(this, 'updateUserNotifyProps', success, error));
+ }
+
+ updateRoles = (userId, newRoles, success, error) => {
+ var data = {
+ user_id: userId,
+ new_roles: newRoles
+ };
+
+ request.
+ post(`${this.getUsersRoute()}/update_roles`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'updateRoles', success, error));
+
+ this.track('api', 'api_users_update_roles');
+ }
+
+ updateActive = (userId, active, success, error) => {
+ var data = {};
+ data.user_id = userId;
+ data.active = '' + active;
+
+ request.
+ post(`${this.getUsersRoute()}/update_active`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'updateActive', success, error));
+
+ this.track('api', 'api_users_update_roles');
+ }
+
+ sendPasswordReset = (email, success, error) => {
+ var data = {};
+ data.email = email;
+
+ request.
+ post(`${this.getUsersRoute()}/send_password_reset`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'sendPasswordReset', success, error));
+
+ this.track('api', 'api_users_send_password_reset');
+ }
+
+ resetPassword = (code, newPassword, success, error) => {
+ var data = {};
+ data.new_password = newPassword;
+ data.code = code;
+
+ request.
+ post(`${this.getUsersRoute()}/reset_password`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'resetPassword', success, error));
+
+ this.track('api', 'api_users_reset_password');
+ }
+
+ emailToOAuth = (email, password, service, success, error) => {
+ var data = {};
+ data.password = password;
+ data.email = email;
+ data.service = service;
+
+ request.
+ post(`${this.getUsersRoute()}/claim/email_to_oauth`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'emailToOAuth', success, error));
+
+ this.track('api', 'api_users_email_to_oauth');
+ }
+
+ oauthToEmail = (email, password, success, error) => {
+ var data = {};
+ data.password = password;
+ data.email = email;
+
+ request.
+ post(`${this.getUsersRoute()}/claim/oauth_to_email`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'oauthToEmail', success, error));
+
+ this.track('api', 'api_users_oauth_to_email');
+ }
+
+ emailToLdap = (email, password, ldapId, ldapPassword, success, error) => {
+ var data = {};
+ data.email_password = password;
+ data.email = email;
+ data.ldap_id = ldapId;
+ data.ldap_password = ldapPassword;
+
+ request.
+ post(`${this.getUsersRoute()}/claim/email_to_ldap`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'emailToLdap', success, error));
+
+ this.track('api', 'api_users_email_to_ldap');
+ }
+
+ ldapToEmail = (email, emailPassword, ldapPassword, success, error) => {
+ var data = {};
+ data.email = email;
+ data.ldap_password = ldapPassword;
+ data.email_password = emailPassword;
+
+ request.
+ post(`${this.getUsersRoute()}/claim/ldap_to_email`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'ldapToEmail', success, error));
+
+ this.track('api', 'api_users_oauth_to_email');
+ }
+
+ getInitialLoad = (success, error) => {
+ request.
+ get(`${this.getUsersRoute()}/initial_load`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getInitialLoad', success, error));
+ }
+
+ getMe = (success, error) => {
+ request.
+ get(`${this.getUsersRoute()}/me`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getMe', success, error));
+ }
+
+ login = (email, username, password, mfaToken, success, error) => {
+ var outer = this; // eslint-disable-line consistent-this
+
+ request.
+ post(`${this.getUsersRoute()}/login`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({email, password, username, token: mfaToken}).
+ end(this.handleResponse.bind(
+ this,
+ 'login',
+ (data, res) => {
+ if (res && res.header) {
+ outer.token = res.header[HEADER_TOKEN];
+
+ if (outer.useToken) {
+ outer.defaultHeaders[HEADER_AUTH] = `${HEADER_BEARER} ${outer.token}`;
+ }
+ }
+
+ if (success) {
+ success(data, res);
+ }
+ },
+ error
+ ));
+
+ this.track('api', 'api_users_login', '', 'email', email);
+ }
+
+ loginByLdap = (ldapId, password, mfaToken, success, error) => {
+ var outer = this; // eslint-disable-line consistent-this
+
+ request.
+ post(`${this.getUsersRoute()}/login_ldap`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({id: ldapId, password, token: mfaToken}).
+ end(this.handleResponse.bind(
+ this,
+ 'loginByLdap',
+ (data, res) => {
+ if (res && res.header) {
+ outer.token = res.header[HEADER_TOKEN];
+
+ if (outer.useToken) {
+ outer.defaultHeaders[HEADER_AUTH] = `${HEADER_BEARER} ${outer.token}`;
+ }
+ }
+
+ if (success) {
+ success(data, res);
+ }
+ },
+ error
+ ));
+
+ this.track('api', 'api_users_loginLdap', '', 'email', ldapId);
+ }
+
+ logout = (success, error) => {
+ request.
+ post(`${this.getUsersRoute()}/logout`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'logout', success, error));
+
+ this.track('api', 'api_users_logout');
+ }
+
+ checkMfa = (method, loginId, success, error) => {
+ var data = {};
+ data.method = method;
+ data.login_id = loginId;
+
+ request.
+ post(`${this.getUsersRoute()}/mfa`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'checkMfa', success, error));
+
+ this.track('api', 'api_users_oauth_to_email');
+ }
+
+ revokeSession = (altId, success, error) => {
+ request.
+ post(`${this.getUsersRoute()}/revoke_session`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({id: altId}).
+ end(this.handleResponse.bind(this, 'revokeSession', success, error));
+ }
+
+ getSessions = (userId, success, error) => {
+ request.
+ get(`${this.getUserNeededRoute(userId)}/sessions`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getSessions', success, error));
+ }
+
+ getAudits = (userId, success, error) => {
+ request.
+ get(`${this.getUserNeededRoute(userId)}/audits`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getAudits', success, error));
+ }
+
+ getDirectProfiles = (success, error) => {
+ request.
+ get(`${this.getUsersRoute()}/direct_profiles`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getDirectProfiles', success, error));
+ }
+
+ getProfiles = (success, error) => {
+ request.
+ get(`${this.getUsersRoute()}/profiles/${this.getTeamId()}`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getProfiles', success, error));
+ }
+
+ getProfilesForTeam = (teamId, success, error) => {
+ request.
+ get(`${this.getUsersRoute()}/profiles/${teamId}?skip_direct=true`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getProfilesForTeam', success, error));
+ }
+
+ getStatuses = (ids, success, error) => {
+ request.
+ post(`${this.getUsersRoute()}/status`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(ids).
+ end(this.handleResponse.bind(this, 'getStatuses', success, error));
+ }
+
+ verifyEmail = (uid, hid, success, error) => {
+ request.
+ post(`${this.getUsersRoute()}/verify_email`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({uid, hid}).
+ end(this.handleResponse.bind(this, 'verifyEmail', success, error));
+ }
+
+ resendVerification = (email, success, error) => {
+ request.
+ post(`${this.getUsersRoute()}/resend_verification`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({email}).
+ end(this.handleResponse.bind(this, 'resendVerification', success, error));
+ }
+
+ updateMfa = (token, activate, success, error) => {
+ const data = {};
+ data.activate = activate;
+ data.token = token;
+
+ request.
+ post(`${this.getUsersRoute()}/update_mfa`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'updateMfa', success, error));
+ }
+
+ uploadProfileImage = (image, success, error) => {
+ request.
+ post(`${this.getUsersRoute()}/newimage`).
+ set(this.defaultHeaders).
+ attach('image', image, image.name).
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'uploadProfileImage', success, error));
+ }
+
+ // Channel Routes Section
+
+ createChannel = (channel, success, error) => {
+ request.
+ post(`${this.getChannelsRoute()}/create`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(channel).
+ end(this.handleResponse.bind(this, 'createChannel', success, error));
+
+ this.track('api', 'api_channels_create', channel.type, 'name', channel.name);
+ }
+
+ createDirectChannel = (userId, success, error) => {
+ request.
+ post(`${this.getChannelsRoute()}/create_direct`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({user_id: userId}).
+ end(this.handleResponse.bind(this, 'createDirectChannel', success, error));
+ }
+
+ updateChannel = (channel, success, error) => {
+ request.
+ post(`${this.getChannelsRoute()}/update`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(channel).
+ end(this.handleResponse.bind(this, 'updateChannel', success, error));
+
+ this.track('api', 'api_channels_update');
+ }
+
+ updateChannelHeader = (channelId, header, success, error) => {
+ const data = {
+ channel_id: channelId,
+ channel_header: header
+ };
+
+ request.
+ post(`${this.getChannelsRoute()}/update_header`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'updateChannel', success, error));
+
+ this.track('api', 'api_channels_header');
+ }
+
+ updateChannelPurpose = (channelId, purpose, success, error) => {
+ const data = {
+ channel_id: channelId,
+ channel_purpose: purpose
+ };
+
+ request.
+ post(`${this.getChannelsRoute()}/update_purpose`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'updateChannelPurpose', success, error));
+
+ this.track('api', 'api_channels_purpose');
+ }
+
+ updateChannelNotifyProps = (data, success, error) => {
+ request.
+ post(`${this.getChannelsRoute()}/update_notify_props`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'updateChannelNotifyProps', success, error));
+ }
+
+ leaveChannel = (channelId, success, error) => {
+ request.
+ post(`${this.getChannelNeededRoute(channelId)}/leave`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'leaveChannel', success, error));
+
+ this.track('api', 'api_channels_leave');
+ }
+
+ joinChannel = (channelId, success, error) => {
+ request.
+ post(`${this.getChannelNeededRoute(channelId)}/join`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'joinChannel', success, error));
+
+ this.track('api', 'api_channels_join');
+ }
+
+ deleteChannel = (channelId, success, error) => {
+ request.
+ post(`${this.getChannelNeededRoute(channelId)}/delete`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'deleteChannel', success, error));
+
+ this.track('api', 'api_channels_delete');
+ }
+
+ updateLastViewedAt = (channelId, success, error) => {
+ request.
+ post(`${this.getChannelNeededRoute(channelId)}/update_last_viewed_at`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'updateLastViewedAt', success, error));
+ }
+
+ getChannels = (success, error) => {
+ request.
+ get(`${this.getChannelsRoute()}/`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getChannels', success, error));
+ }
+
+ getChannel = (channelId, success, error) => {
+ request.
+ get(`${this.getChannelNeededRoute(channelId)}/`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getChannel', success, error));
+
+ this.track('api', 'api_channel_get');
+ }
+
+ getMoreChannels = (success, error) => {
+ request.
+ get(`${this.getChannelsRoute()}/more`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getMoreChannels', success, error));
+ }
+
+ getChannelCounts = (success, error) => {
+ request.
+ get(`${this.getChannelsRoute()}/counts`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getChannelCounts', success, error));
+ }
+
+ getChannelExtraInfo = (channelId, memberLimit, success, error) => {
+ var url = `${this.getChannelNeededRoute(channelId)}/extra_info`;
+ if (memberLimit) {
+ url += '/' + memberLimit;
+ }
+
+ request.
+ get(url).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getChannelExtraInfo', success, error));
+ }
+
+ addChannelMember = (channelId, userId, success, error) => {
+ request.
+ post(`${this.getChannelNeededRoute(channelId)}/add`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({user_id: userId}).
+ end(this.handleResponse.bind(this, 'addChannelMember', success, error));
+
+ this.track('api', 'api_channels_add_member');
+ }
+
+ removeChannelMember = (channelId, userId, success, error) => {
+ request.
+ post(`${this.getChannelNeededRoute(channelId)}/remove`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({user_id: userId}).
+ end(this.handleResponse.bind(this, 'removeChannelMember', success, error));
+
+ this.track('api', 'api_channels_remove_member');
+ }
+
+ // Routes for Commands
+
+ listCommands = (success, error) => {
+ request.
+ get(`${this.getCommandsRoute()}/list`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'listCommands', success, error));
+ }
+
+ executeCommand = (channelId, command, suggest, success, error) => {
+ request.
+ post(`${this.getCommandsRoute()}/execute`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({channelId, command, suggest: '' + suggest}).
+ end(this.handleResponse.bind(this, 'executeCommand', success, error));
+ }
+
+ addCommand = (command, success, error) => {
+ request.
+ post(`${this.getCommandsRoute()}/create`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(command).
+ end(this.handleResponse.bind(this, 'addCommand', success, error));
+ }
+
+ deleteCommand = (commandId, success, error) => {
+ request.
+ post(`${this.getCommandsRoute()}/delete`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({id: commandId}).
+ end(this.handleResponse.bind(this, 'deleteCommand', success, error));
+ }
+
+ listTeamCommands = (success, error) => {
+ request.
+ get(`${this.getCommandsRoute()}/list_team_commands`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'listTeamCommands', success, error));
+ }
+
+ regenCommandToken = (commandId, suggest, success, error) => {
+ request.
+ post(`${this.getCommandsRoute()}/regen_token`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({id: commandId}).
+ end(this.handleResponse.bind(this, 'regenCommandToken', success, error));
+ }
+
+ // Routes for Posts
+
+ createPost = (post, success, error) => {
+ request.
+ post(`${this.getPostsRoute(post.channel_id)}/create`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(post).
+ end(this.handleResponse.bind(this, 'createPost', success, error));
+
+ this.track('api', 'api_posts_create', post.channel_id, 'length', post.message.length);
+ }
+
+ getPostById = (postId, success, error) => {
+ request.
+ get(`${this.getTeamNeededRoute()}/posts/${postId}`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getPostById', success, error));
+ }
+
+ getPost = (channelId, postId, success, error) => {
+ request.
+ get(`${this.getChannelNeededRoute(channelId)}/posts/${postId}/get`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getPost', success, error));
+ }
+
+ updatePost = (post, success, error) => {
+ request.
+ post(`${this.getPostsRoute(post.channel_id)}/update`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(post).
+ end(this.handleResponse.bind(this, 'updatePost', success, error));
+
+ this.track('api', 'api_posts_update');
+ }
+
+ deletePost = (channelId, postId, success, error) => {
+ request.
+ post(`${this.getChannelNeededRoute(channelId)}/posts/${postId}/delete`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'deletePost', success, error));
+
+ this.track('api', 'api_posts_delete');
+ }
+
+ search = (terms, success, error) => {
+ request.
+ get(`${this.getTeamNeededRoute()}/posts/search`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ query({terms}).
+ end(this.handleResponse.bind(this, 'search', success, error));
+
+ this.track('api', 'api_posts_search');
+ }
+
+ getPostsPage = (channelId, offset, limit, success, error) => {
+ request.
+ get(`${this.getPostsRoute(channelId)}/page/${offset}/${limit}`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getPostsPage', success, error));
+ }
+
+ getPosts = (channelId, since, success, error) => {
+ request.
+ get(`${this.getPostsRoute(channelId)}/since/${since}`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getPosts', success, error));
+ }
+
+ getPostsBefore = (channelId, postId, offset, numPost, success, error) => {
+ request.
+ get(`${this.getPostsRoute(channelId)}/${postId}/before/${offset}/${numPost}`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getPostsBefore', success, error));
+ }
+
+ getPostsAfter = (channelId, postId, offset, numPost, success, error) => {
+ request.
+ get(`${this.getPostsRoute(channelId)}/${postId}/after/${offset}/${numPost}`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getPostsAfter', success, error));
+ }
+
+ // Routes for Files
+
+ getFileInfo = (filename, success, error) => {
+ request.
+ get(`${this.getFilesRoute()}/get_info${filename}`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getFileInfo', success, error));
+ }
+
+ getPublicLink = (data, success, error) => {
+ request.
+ post(`${this.getFilesRoute()}/get_public_link`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(data).
+ end(this.handleResponse.bind(this, 'getPublicLink', success, error));
+ }
+
+ uploadFile = (file, filename, channelId, clientId, success, error) => {
+ return request.
+ post(`${this.getFilesRoute()}/upload`).
+ set(this.defaultHeaders).
+ attach('files', file, filename).
+ field('channel_id', channelId).
+ field('client_ids', clientId).
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'uploadFile', success, error));
+ }
+
+ // Routes for OAuth
+
+ registerOAuthApp = (app, success, error) => {
+ request.
+ post(`${this.getOAuthRoute()}/register`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(app).
+ end(this.handleResponse.bind(this, 'registerOAuthApp', success, error));
+
+ this.track('api', 'api_apps_register');
+ }
+
+ allowOAuth2 = (responseType, clientId, redirectUri, state, scope, success, error) => {
+ request.
+ get(`${this.getOAuthRoute()}/allow`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ query({response_type: responseType}).
+ query({client_id: clientId}).
+ query({redirect_uri: redirectUri}).
+ query({scope}).
+ query({state}).
+ end(this.handleResponse.bind(this, 'allowOAuth2', success, error));
+ }
+
+ // Routes for Hooks
+
+ addIncomingHook = (hook, success, error) => {
+ request.
+ post(`${this.getHooksRoute()}/incoming/create`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(hook).
+ end(this.handleResponse.bind(this, 'addIncomingHook', success, error));
+ }
+
+ deleteIncomingHook = (hookId, success, error) => {
+ request.
+ post(`${this.getHooksRoute()}/incoming/delete`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({id: hookId}).
+ end(this.handleResponse.bind(this, 'deleteIncomingHook', success, error));
+ }
+
+ listIncomingHooks = (success, error) => {
+ request.
+ get(`${this.getHooksRoute()}/incoming/list`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'listIncomingHooks', success, error));
+ }
+
+ addOutgoingHook = (hook, success, error) => {
+ request.
+ post(`${this.getHooksRoute()}/outgoing/create`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(hook).
+ end(this.handleResponse.bind(this, 'addOutgoingHook', success, error));
+ }
+
+ deleteOutgoingHook = (hookId, success, error) => {
+ request.
+ post(`${this.getHooksRoute()}/outgoing/delete`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({id: hookId}).
+ end(this.handleResponse.bind(this, 'deleteOutgoingHook', success, error));
+ }
+
+ listOutgoingHooks = (success, error) => {
+ request.
+ get(`${this.getHooksRoute()}/outgoing/list`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'listOutgoingHooks', success, error));
+ }
+
+ regenOutgoingHookToken = (hookId, success, error) => {
+ request.
+ post(`${this.getHooksRoute()}/outgoing/regen_token`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send({id: hookId}).
+ end(this.handleResponse.bind(this, 'regenOutgoingHookToken', success, error));
+ }
+
+ //Routes for Prefrecnes
+
+ getAllPreferences = (success, error) => {
+ request.
+ get(`${this.getBaseRoute()}/preferences/`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getAllPreferences', success, error));
+ }
+
+ savePreferences = (preferences, success, error) => {
+ request.
+ post(`${this.getBaseRoute()}/preferences/save`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ send(preferences).
+ end(this.handleResponse.bind(this, 'savePreferences', success, error));
+ }
+
+ getPreferenceCategory = (category, success, error) => {
+ request.
+ get(`${this.getBaseRoute()}/preferences/${category}`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getPreferenceCategory', success, error));
+ }
+}
diff --git a/webapp/components/activity_log_modal.jsx b/webapp/components/activity_log_modal.jsx
index f1dd4a26a..d3e5ce66d 100644
--- a/webapp/components/activity_log_modal.jsx
+++ b/webapp/components/activity_log_modal.jsx
@@ -3,7 +3,7 @@
import $ from 'jquery';
import UserStore from 'stores/user_store.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import {Modal} from 'react-bootstrap';
import LoadingScreen from './loading_screen.jsx';
diff --git a/webapp/components/admin_console/admin_navbar_dropdown.jsx b/webapp/components/admin_console/admin_navbar_dropdown.jsx
index 729d4b14d..dd56411f4 100644
--- a/webapp/components/admin_console/admin_navbar_dropdown.jsx
+++ b/webapp/components/admin_console/admin_navbar_dropdown.jsx
@@ -3,27 +3,20 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
-import * as Utils from 'utils/utils.jsx';
-import TeamStore from 'stores/team_store.jsx';
import Constants from 'utils/constants.jsx';
+import * as GlobalActions from 'action_creators/global_actions.jsx';
import {FormattedMessage} from 'react-intl';
import {Link} from 'react-router';
-function getStateFromStores() {
- return {currentTeam: TeamStore.getCurrent()};
-}
-
import React from 'react';
export default class AdminNavbarDropdown extends React.Component {
constructor(props) {
super(props);
this.blockToggle = false;
-
- this.state = getStateFromStores();
}
componentDidMount() {
@@ -64,24 +57,27 @@ export default class AdminNavbarDropdown extends React.Component {
>
<li>
<Link
- to={'/' + this.state.currentTeam.name + '/channels/town-square'}
+ to={'/select_team'}
>
<FormattedMessage
id='admin.nav.switch'
defaultMessage='Switch to {display_name}'
values={{
- display_name: this.state.currentTeam.display_name
+ display_name: global.window.mm_config.SiteName
}}
/>
</Link>
</li>
<li>
- <Link to={Utils.getTeamURLFromAddressBar() + '/logout'}>
+ <a
+ href='#'
+ onClick={GlobalActions.emitUserLoggedOutEvent}
+ >
<FormattedMessage
id='admin.nav.logout'
defaultMessage='Logout'
/>
- </Link>
+ </a>
</li>
</ul>
</li>
diff --git a/webapp/components/admin_console/admin_sidebar_header.jsx b/webapp/components/admin_console/admin_sidebar_header.jsx
index 2e6252075..400730030 100644
--- a/webapp/components/admin_console/admin_sidebar_header.jsx
+++ b/webapp/components/admin_console/admin_sidebar_header.jsx
@@ -4,6 +4,7 @@
import $ from 'jquery';
import AdminNavbarDropdown from './admin_navbar_dropdown.jsx';
import UserStore from 'stores/user_store.jsx';
+import Client from 'utils/web_client.jsx';
import {FormattedMessage} from 'react-intl';
@@ -41,7 +42,7 @@ export default class SidebarHeader extends React.Component {
profilePicture = (
<img
className='user__picture'
- src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at}
+ src={Client.getUsersRoute() + '/' + me.id + '/image?time=' + me.update_at}
/>
);
}
diff --git a/webapp/components/admin_console/compliance_reports.jsx b/webapp/components/admin_console/compliance_reports.jsx
index 84def2bce..702c1a969 100644
--- a/webapp/components/admin_console/compliance_reports.jsx
+++ b/webapp/components/admin_console/compliance_reports.jsx
@@ -7,7 +7,7 @@ import * as Utils from '../../utils/utils.jsx';
import AdminStore from '../../stores/admin_store.jsx';
import UserStore from '../../stores/user_store.jsx';
-import * as Client from '../../utils/client.jsx';
+import * as Client from '../../utils/web_client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
import {FormattedMessage, FormattedDate, FormattedTime} from 'react-intl';
@@ -153,7 +153,7 @@ export default class ComplianceReports extends React.Component {
var download = '';
if (report.status === 'finished') {
download = (
- <a href={'/api/v1/admin/download_compliance_report/' + report.id}>
+ <a href={Client.getAdminRoute() + '/download_compliance_report/' + report.id}>
<FormattedMessage
id='admin.compliance_table.download'
defaultMessage='Download'
diff --git a/webapp/components/admin_console/compliance_settings.jsx b/webapp/components/admin_console/compliance_settings.jsx
index 206bb0faa..b127634e8 100644
--- a/webapp/components/admin_console/compliance_settings.jsx
+++ b/webapp/components/admin_console/compliance_settings.jsx
@@ -2,7 +2,7 @@
// See License.txt for license information.
import $ from 'jquery';
-import * as Client from '../../utils/client.jsx';
+import * as Client from '../../utils/web_client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
import * as Utils from '../../utils/utils.jsx';
diff --git a/webapp/components/admin_console/email_settings.jsx b/webapp/components/admin_console/email_settings.jsx
index 8df48b206..1fa75ead9 100644
--- a/webapp/components/admin_console/email_settings.jsx
+++ b/webapp/components/admin_console/email_settings.jsx
@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import crypto from 'crypto';
import ConnectionSecurityDropdownSetting from './connection_security_dropdown_setting.jsx';
diff --git a/webapp/components/admin_console/gitlab_settings.jsx b/webapp/components/admin_console/gitlab_settings.jsx
index 7fdedde13..747905ac6 100644
--- a/webapp/components/admin_console/gitlab_settings.jsx
+++ b/webapp/components/admin_console/gitlab_settings.jsx
@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl';
diff --git a/webapp/components/admin_console/image_settings.jsx b/webapp/components/admin_console/image_settings.jsx
index 576ff18fd..023e9af3b 100644
--- a/webapp/components/admin_console/image_settings.jsx
+++ b/webapp/components/admin_console/image_settings.jsx
@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import crypto from 'crypto';
diff --git a/webapp/components/admin_console/ldap_settings.jsx b/webapp/components/admin_console/ldap_settings.jsx
index dd6e4338f..01402a588 100644
--- a/webapp/components/admin_console/ldap_settings.jsx
+++ b/webapp/components/admin_console/ldap_settings.jsx
@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as Utils from 'utils/utils.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
@@ -61,6 +61,7 @@ class LdapSettings extends React.Component {
config.LdapSettings.BindPassword = this.refs.BindPassword.value.trim();
config.LdapSettings.FirstNameAttribute = this.refs.FirstNameAttribute.value.trim();
config.LdapSettings.LastNameAttribute = this.refs.LastNameAttribute.value.trim();
+ config.LdapSettings.NicknameAttribute = this.refs.NicknameAttribute.value.trim();
config.LdapSettings.EmailAttribute = this.refs.EmailAttribute.value.trim();
config.LdapSettings.UsernameAttribute = this.refs.UsernameAttribute.value.trim();
config.LdapSettings.IdAttribute = this.refs.IdAttribute.value.trim();
@@ -441,6 +442,35 @@ class LdapSettings extends React.Component {
<div className='form-group'>
<label
className='control-label col-sm-4'
+ htmlFor='NicknameAttribute'
+ >
+ <FormattedMessage
+ id='admin.ldap.nicknameAttrTitle'
+ defaultMessage='Nickname Attribute:'
+ />
+ </label>
+ <div className='col-sm-8'>
+ <input
+ type='text'
+ className='form-control'
+ id='NicknameAttribute'
+ ref='NicknameAttribute'
+ placeholder={Utils.localizeMessage('admin.ldap.nicknameAttrEx', 'Ex "nickname"')}
+ defaultValue={this.props.config.LdapSettings.NicknameAttribute}
+ onChange={this.handleChange}
+ disabled={!this.state.enable}
+ />
+ <p className='help-text'>
+ <FormattedMessage
+ id='admin.ldap.nicknameAttrDesc'
+ defaultMessage='(Optional) The attribute in the LDAP server that will be used to populate the nickname of users in Mattermost.'
+ />
+ </p>
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
htmlFor='EmailAttribute'
>
<FormattedMessage
diff --git a/webapp/components/admin_console/legal_and_support_settings.jsx b/webapp/components/admin_console/legal_and_support_settings.jsx
index bbbb3713c..9f72f5fdf 100644
--- a/webapp/components/admin_console/legal_and_support_settings.jsx
+++ b/webapp/components/admin_console/legal_and_support_settings.jsx
@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';
diff --git a/webapp/components/admin_console/license_settings.jsx b/webapp/components/admin_console/license_settings.jsx
index 20e2fc83a..f2c511e44 100644
--- a/webapp/components/admin_console/license_settings.jsx
+++ b/webapp/components/admin_console/license_settings.jsx
@@ -4,7 +4,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl';
@@ -54,10 +54,7 @@ class LicenseSettings extends React.Component {
$('#upload-button').button('loading');
- const formData = new FormData();
- formData.append('license', file, file.name);
-
- Client.uploadLicenseFile(formData,
+ Client.uploadLicenseFile(file,
() => {
Utils.clearFileInput(element[0]);
$('#upload-button').button('reset');
diff --git a/webapp/components/admin_console/log_settings.jsx b/webapp/components/admin_console/log_settings.jsx
index 5aa3ca1e0..061c2b6e3 100644
--- a/webapp/components/admin_console/log_settings.jsx
+++ b/webapp/components/admin_console/log_settings.jsx
@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';
diff --git a/webapp/components/admin_console/privacy_settings.jsx b/webapp/components/admin_console/privacy_settings.jsx
index a312dddca..5045a6d31 100644
--- a/webapp/components/admin_console/privacy_settings.jsx
+++ b/webapp/components/admin_console/privacy_settings.jsx
@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';
diff --git a/webapp/components/admin_console/rate_settings.jsx b/webapp/components/admin_console/rate_settings.jsx
index f3fb1742c..de7a40e6b 100644
--- a/webapp/components/admin_console/rate_settings.jsx
+++ b/webapp/components/admin_console/rate_settings.jsx
@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';
diff --git a/webapp/components/admin_console/reset_password_modal.jsx b/webapp/components/admin_console/reset_password_modal.jsx
index f80c740e3..5133f3f28 100644
--- a/webapp/components/admin_console/reset_password_modal.jsx
+++ b/webapp/components/admin_console/reset_password_modal.jsx
@@ -2,7 +2,7 @@
// See License.txt for license information.
import ReactDOM from 'react-dom';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
import {Modal} from 'react-bootstrap';
@@ -40,12 +40,9 @@ class ResetPasswordModal extends React.Component {
this.setState({serverError: null});
- var data = {};
- data.new_password = password;
- data.name = this.props.team.name;
- data.user_id = this.props.user.id;
-
- Client.resetPassword(data,
+ Client.adminResetPassword(
+ this.props.user.id,
+ password,
() => {
this.props.onModalSubmit(ReactDOM.findDOMNode(this.refs.password).value);
},
@@ -159,4 +156,4 @@ ResetPasswordModal.propTypes = {
onModalDismissed: React.PropTypes.func
};
-export default injectIntl(ResetPasswordModal); \ No newline at end of file
+export default injectIntl(ResetPasswordModal);
diff --git a/webapp/components/admin_console/service_settings.jsx b/webapp/components/admin_console/service_settings.jsx
index 2c3f4081c..90b6a39b4 100644
--- a/webapp/components/admin_console/service_settings.jsx
+++ b/webapp/components/admin_console/service_settings.jsx
@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl';
diff --git a/webapp/components/admin_console/sql_settings.jsx b/webapp/components/admin_console/sql_settings.jsx
index 33bb2cece..f2e005b83 100644
--- a/webapp/components/admin_console/sql_settings.jsx
+++ b/webapp/components/admin_console/sql_settings.jsx
@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import crypto from 'crypto';
diff --git a/webapp/components/admin_console/team_settings.jsx b/webapp/components/admin_console/team_settings.jsx
index 6c9828351..bbb7ec3c4 100644
--- a/webapp/components/admin_console/team_settings.jsx
+++ b/webapp/components/admin_console/team_settings.jsx
@@ -2,7 +2,7 @@
// See License.txt for license information.
import $ from 'jquery';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx';
@@ -42,6 +42,7 @@ class TeamSettings extends React.Component {
this.handleImageSubmit = this.handleImageSubmit.bind(this);
this.uploading = false;
+ this.timestamp = 0;
this.state = {
saveNeeded: false,
@@ -53,7 +54,7 @@ class TeamSettings extends React.Component {
componentWillMount() {
if (global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.CustomBrand === 'true') {
- $.get('/api/v1/admin/get_brand_image').done(() => this.setState({brandImageExists: true}));
+ $.get(Client.getAdminRoute() + '/get_brand_image').done(() => this.setState({brandImageExists: true}));
}
}
@@ -89,6 +90,7 @@ class TeamSettings extends React.Component {
if (element.prop('files').length > 0) {
this.setState({fileSelected: true, brandImage: element.prop('files')[0]});
}
+ $('#upload-button').button('reset');
}
handleSubmit(e) {
@@ -100,8 +102,8 @@ class TeamSettings extends React.Component {
config.TeamSettings.RestrictCreationToDomains = this.refs.RestrictCreationToDomains.value.trim();
config.TeamSettings.EnableTeamCreation = this.refs.EnableTeamCreation.checked;
config.TeamSettings.EnableUserCreation = this.refs.EnableUserCreation.checked;
+ config.TeamSettings.EnableOpenServer = this.refs.EnableOpenServer.checked;
config.TeamSettings.RestrictTeamNames = this.refs.RestrictTeamNames.checked;
- config.TeamSettings.EnableTeamListing = this.refs.EnableTeamListing.checked;
if (this.refs.EnableCustomBrand) {
config.TeamSettings.EnableCustomBrand = this.refs.EnableCustomBrand.checked;
@@ -155,6 +157,7 @@ class TeamSettings extends React.Component {
Client.uploadBrandImage(this.state.brandImage,
() => {
$('#upload-button').button('complete');
+ this.timestamp = Utils.getTimestamp();
this.setState({brandImageExists: true, brandImage: null});
this.uploading = false;
},
@@ -193,7 +196,7 @@ class TeamSettings extends React.Component {
img = (
<img
className='brand-img'
- src='/api/v1/admin/get_brand_image'
+ src={Client.getAdminRoute() + '/get_brand_image?t=' + this.timestamp}
/>
);
} else {
@@ -537,6 +540,53 @@ class TeamSettings extends React.Component {
<div className='form-group'>
<label
className='control-label col-sm-4'
+ htmlFor='EnableOpenServer'
+ >
+ <FormattedMessage
+ id='admin.team.openServerTitle'
+ defaultMessage='Enable Open Server: '
+ />
+ </label>
+ <div className='col-sm-8'>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnableOpenServer'
+ value='true'
+ ref='EnableOpenServer'
+ defaultChecked={this.props.config.TeamSettings.EnableOpenServer}
+ onChange={this.handleChange}
+ />
+ <FormattedMessage
+ id='admin.team.true'
+ defaultMessage='true'
+ />
+ </label>
+ <label className='radio-inline'>
+ <input
+ type='radio'
+ name='EnableOpenServer'
+ value='false'
+ defaultChecked={!this.props.config.TeamSettings.EnableOpenServer}
+ onChange={this.handleChange}
+ />
+ <FormattedMessage
+ id='admin.team.false'
+ defaultMessage='false'
+ />
+ </label>
+ <p className='help-text'>
+ <FormattedMessage
+ id='admin.team.openServerDescription'
+ defaultMessage='When true, anyone can signup for a user account on this server without the need to be invited.'
+ />
+ </p>
+ </div>
+ </div>
+
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
htmlFor='RestrictCreationToDomains'
>
<FormattedMessage
@@ -610,53 +660,6 @@ class TeamSettings extends React.Component {
</div>
</div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='EnableTeamListing'
- >
- <FormattedMessage
- id='admin.team.dirTitle'
- defaultMessage='Enable Team Directory: '
- />
- </label>
- <div className='col-sm-8'>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableTeamListing'
- value='true'
- ref='EnableTeamListing'
- defaultChecked={this.props.config.TeamSettings.EnableTeamListing}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.true'
- defaultMessage='true'
- />
- </label>
- <label className='radio-inline'>
- <input
- type='radio'
- name='EnableTeamListing'
- value='false'
- defaultChecked={!this.props.config.TeamSettings.EnableTeamListing}
- onChange={this.handleChange}
- />
- <FormattedMessage
- id='admin.team.false'
- defaultMessage='false'
- />
- </label>
- <p className='help-text'>
- <FormattedMessage
- id='admin.team.dirDesc'
- defaultMessage='When true, teams that are configured to show in team directory will show on main page inplace of creating a new team.'
- />
- </p>
- </div>
- </div>
-
{brand}
<div className='form-group'>
diff --git a/webapp/components/admin_console/team_users.jsx b/webapp/components/admin_console/team_users.jsx
index 8b37bd237..2b0e6af0a 100644
--- a/webapp/components/admin_console/team_users.jsx
+++ b/webapp/components/admin_console/team_users.jsx
@@ -1,7 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import LoadingScreen from '../loading_screen.jsx';
import UserItem from './user_item.jsx';
import ResetPasswordModal from './reset_password_modal.jsx';
diff --git a/webapp/components/admin_console/user_item.jsx b/webapp/components/admin_console/user_item.jsx
index 5bd05d063..b8f21d77e 100644
--- a/webapp/components/admin_console/user_item.jsx
+++ b/webapp/components/admin_console/user_item.jsx
@@ -1,13 +1,13 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as Utils from 'utils/utils.jsx';
import UserStore from 'stores/user_store.jsx';
import ConfirmModal from '../confirm_modal.jsx';
import TeamStore from 'stores/team_store.jsx';
-import {FormattedMessage} from 'react-intl';
+import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
import React from 'react';
import {browserHistory} from 'react-router';
@@ -22,6 +22,7 @@ export default class UserItem extends React.Component {
this.handleMakeAdmin = this.handleMakeAdmin.bind(this);
this.handleMakeSystemAdmin = this.handleMakeSystemAdmin.bind(this);
this.handleResetPassword = this.handleResetPassword.bind(this);
+ this.handleResetMfa = this.handleResetMfa.bind(this);
this.handleDemote = this.handleDemote.bind(this);
this.handleDemoteSubmit = this.handleDemoteSubmit.bind(this);
this.handleDemoteCancel = this.handleDemoteCancel.bind(this);
@@ -40,12 +41,9 @@ export default class UserItem extends React.Component {
if (this.props.user.id === me.id) {
this.handleDemote(this.props.user, '');
} else {
- const data = {
- user_id: this.props.user.id,
- new_roles: ''
- };
-
- Client.updateRoles(data,
+ Client.updateRoles(
+ this.props.user.id,
+ '',
() => {
this.props.refreshProfiles();
},
@@ -86,12 +84,9 @@ export default class UserItem extends React.Component {
if (this.props.user.id === me.id) {
this.handleDemote(this.props.user, 'admin');
} else {
- const data = {
- user_id: this.props.user.id,
- new_roles: 'admin'
- };
-
- Client.updateRoles(data,
+ Client.updateRoles(
+ this.props.user.id,
+ 'admin',
() => {
this.props.refreshProfiles();
},
@@ -104,12 +99,10 @@ export default class UserItem extends React.Component {
handleMakeSystemAdmin(e) {
e.preventDefault();
- const data = {
- user_id: this.props.user.id,
- new_roles: 'system_admin'
- };
- Client.updateRoles(data,
+ Client.updateRoles(
+ this.props.user.id,
+ 'system_admin',
() => {
this.props.refreshProfiles();
},
@@ -124,6 +117,19 @@ export default class UserItem extends React.Component {
this.props.doPasswordReset(this.props.user);
}
+ handleResetMfa(e) {
+ e.preventDefault();
+
+ Client.adminResetMfa(this.props.user.id,
+ () => {
+ this.props.refreshProfiles();
+ },
+ (err) => {
+ this.setState({serverError: err.message});
+ }
+ );
+ }
+
handleDemote(user, role) {
this.setState({
serverError: this.state.serverError,
@@ -143,12 +149,9 @@ export default class UserItem extends React.Component {
}
handleDemoteSubmit() {
- const data = {
- user_id: this.props.user.id,
- new_roles: this.state.role
- };
-
- Client.updateRoles(data,
+ Client.updateRoles(
+ this.props.user.id,
+ this.state.role,
() => {
this.setState({
serverError: null,
@@ -211,10 +214,15 @@ export default class UserItem extends React.Component {
const email = user.email;
let showMakeMember = user.roles === 'admin' || user.roles === 'system_admin';
- let showMakeAdmin = user.roles === '' || user.roles === 'system_admin';
+
+ //let showMakeAdmin = user.roles === '' || user.roles === 'system_admin';
+ let showMakeAdmin = false;
+
let showMakeSystemAdmin = user.roles === '' || user.roles === 'admin';
let showMakeActive = false;
let showMakeNotActive = user.roles !== 'system_admin';
+ let mfaEnabled = global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.MFA === 'true' && global.window.mm_config.EnableMultifactorAuthentication === 'true';
+ let showMfaReset = mfaEnabled && user.mfa_active;
if (user.delete_at > 0) {
currentRoles = (
@@ -319,6 +327,64 @@ export default class UserItem extends React.Component {
</li>
);
}
+
+ let mfaReset = null;
+ if (showMfaReset) {
+ mfaReset = (
+ <li role='presentation'>
+ <a
+ role='menuitem'
+ href='#'
+ onClick={this.handleResetMfa}
+ >
+ <FormattedMessage
+ id='admin.user_item.resetMfa'
+ defaultMessage='Remove MFA'
+ />
+ </a>
+ </li>
+ );
+ }
+
+ let mfaActiveText;
+ if (mfaEnabled) {
+ if (user.mfa_active) {
+ mfaActiveText = (
+ <FormattedHTMLMessage
+ id='admin.user_item.mfaYes'
+ defaultMessage=', <strong>MFA</strong>: Yes'
+ />
+ );
+ } else {
+ mfaActiveText = (
+ <FormattedHTMLMessage
+ id='admin.user_item.mfaNo'
+ defaultMessage=', <strong>MFA</strong>: No'
+ />
+ );
+ }
+ }
+
+ let authServiceText;
+ if (user.auth_service) {
+ authServiceText = (
+ <FormattedHTMLMessage
+ id='admin.user_item.authServiceNotEmail'
+ defaultMessage=', <strong>Sign-in Method:</strong> {service}'
+ values={{
+ service: Utils.toTitleCase(user.auth_service)
+ }}
+ />
+ );
+ } else {
+ authServiceText = (
+ <FormattedHTMLMessage
+ id='admin.user_item.authServiceEmail'
+ defaultMessage=', <strong>Sign-in Method:</strong> Email'
+ />
+ );
+ }
+
const me = UserStore.getCurrentUser();
let makeDemoteModal = null;
if (this.props.user.id === me.id) {
@@ -368,13 +434,23 @@ export default class UserItem extends React.Component {
<div className='more-modal__row'>
<img
className='more-modal__image pull-left'
- src={`/api/v1/users/${user.id}/image?time=${user.update_at}`}
+ src={`${Client.getUsersRoute()}/${user.id}/image?time=${user.update_at}`}
height='36'
width='36'
/>
<div className='more-modal__details'>
<div className='more-modal__name'>{Utils.getDisplayName(user)}</div>
- <div className='more-modal__description'>{email}</div>
+ <div className='more-modal__description'>
+ <FormattedHTMLMessage
+ id='admin.user_item.emailTitle'
+ defaultMessage='<strong>Email:</strong> {email}'
+ values={{
+ email
+ }}
+ />
+ {authServiceText}
+ {mfaActiveText}
+ </div>
</div>
<div className='more-modal__actions'>
<div className='dropdown member-drop'>
@@ -397,6 +473,7 @@ export default class UserItem extends React.Component {
{makeActive}
{makeNotActive}
{makeSystemAdmin}
+ {mfaReset}
<li role='presentation'>
<a
role='menuitem'
diff --git a/webapp/components/authorize.jsx b/webapp/components/authorize.jsx
index 01b37f439..b18469568 100644
--- a/webapp/components/authorize.jsx
+++ b/webapp/components/authorize.jsx
@@ -1,7 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
diff --git a/webapp/components/backstage/add_command.jsx b/webapp/components/backstage/add_command.jsx
index 2eb7bdb21..ba9ac4e79 100644
--- a/webapp/components/backstage/add_command.jsx
+++ b/webapp/components/backstage/add_command.jsx
@@ -89,6 +89,8 @@ export default class AddCommand extends React.Component {
/>
)
});
+
+ return;
}
if (!command.url) {
@@ -101,12 +103,14 @@ export default class AddCommand extends React.Component {
/>
)
});
+
+ return;
}
AsyncClient.addCommand(
command,
() => {
- browserHistory.push('/settings/integrations/commands');
+ browserHistory.push('/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands');
},
(err) => {
this.setState({
@@ -251,7 +255,7 @@ export default class AddCommand extends React.Component {
return (
<div className='backstage-content row'>
<BackstageHeader>
- <Link to={'/settings/integrations/commands'}>
+ <Link to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands'}>
<FormattedMessage
id='installed_command.header'
defaultMessage='Slash Commands'
@@ -482,7 +486,7 @@ export default class AddCommand extends React.Component {
<FormError errors={[this.state.serverError, this.state.clientError]}/>
<Link
className='btn btn-sm'
- to={'/settings/integrations/commands'}
+ to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands'}
>
<FormattedMessage
id='add_command.cancel'
diff --git a/webapp/components/backstage/add_incoming_webhook.jsx b/webapp/components/backstage/add_incoming_webhook.jsx
index f68a263be..0f0d49ea7 100644
--- a/webapp/components/backstage/add_incoming_webhook.jsx
+++ b/webapp/components/backstage/add_incoming_webhook.jsx
@@ -5,6 +5,7 @@ import React from 'react';
import * as AsyncClient from 'utils/async_client.jsx';
import {browserHistory} from 'react-router';
+import * as Utils from 'utils/utils.jsx';
import BackstageHeader from './backstage_header.jsx';
import ChannelSelect from 'components/channel_select.jsx';
@@ -69,7 +70,7 @@ export default class AddIncomingWebhook extends React.Component {
AsyncClient.addIncomingHook(
hook,
() => {
- browserHistory.push('/settings/integrations/incoming_webhooks');
+ browserHistory.push('/' + Utils.getTeamNameFromUrl() + '/settings/integrations/incoming_webhooks');
},
(err) => {
this.setState({
@@ -102,7 +103,7 @@ export default class AddIncomingWebhook extends React.Component {
return (
<div className='backstage-content'>
<BackstageHeader>
- <Link to={'/settings/integrations/incoming_webhooks'}>
+ <Link to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/incoming_webhooks'}>
<FormattedMessage
id='installed_incoming_webhooks.header'
defaultMessage='Incoming Webhooks'
diff --git a/webapp/components/backstage/add_outgoing_webhook.jsx b/webapp/components/backstage/add_outgoing_webhook.jsx
index ff5e90e07..245df1604 100644
--- a/webapp/components/backstage/add_outgoing_webhook.jsx
+++ b/webapp/components/backstage/add_outgoing_webhook.jsx
@@ -5,6 +5,7 @@ import React from 'react';
import * as AsyncClient from 'utils/async_client.jsx';
import {browserHistory} from 'react-router';
+import * as Utils from 'utils/utils.jsx';
import BackstageHeader from './backstage_header.jsx';
import ChannelSelect from 'components/channel_select.jsx';
@@ -109,7 +110,7 @@ export default class AddOutgoingWebhook extends React.Component {
AsyncClient.addOutgoingHook(
hook,
() => {
- browserHistory.push('/settings/integrations/outgoing_webhooks');
+ browserHistory.push('/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks');
},
(err) => {
this.setState({
@@ -154,7 +155,7 @@ export default class AddOutgoingWebhook extends React.Component {
return (
<div className='backstage-content'>
<BackstageHeader>
- <Link to={'/settings/integrations/outgoing_webhooks'}>
+ <Link to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks'}>
<FormattedMessage
id='installed_outgoing_webhooks.header'
defaultMessage='Outgoing Webhooks'
@@ -273,7 +274,7 @@ export default class AddOutgoingWebhook extends React.Component {
<FormError errors={[this.state.serverError, this.state.clientError]}/>
<Link
className='btn btn-sm'
- to={'/settings/integrations/outgoing_webhooks'}
+ to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks'}
>
<FormattedMessage
id='add_outgoing_webhook.cancel'
diff --git a/webapp/components/backstage/backstage_navbar.jsx b/webapp/components/backstage/backstage_navbar.jsx
index d2d2da1ed..8352296b7 100644
--- a/webapp/components/backstage/backstage_navbar.jsx
+++ b/webapp/components/backstage/backstage_navbar.jsx
@@ -46,7 +46,7 @@ export default class BackstageNavbar extends React.Component {
<div className='backstage-navbar row'>
<Link
className='backstage-navbar__back'
- to={`/${this.state.team.display_name}/channels/town-square`}
+ to={`/${this.state.team.name}/channels/town-square`}
>
<i className='fa fa-angle-left'/>
<span>
diff --git a/webapp/components/backstage/backstage_sidebar.jsx b/webapp/components/backstage/backstage_sidebar.jsx
index eb84709a3..6f8e0b86a 100644
--- a/webapp/components/backstage/backstage_sidebar.jsx
+++ b/webapp/components/backstage/backstage_sidebar.jsx
@@ -3,6 +3,7 @@
import React from 'react';
+import * as Utils from 'utils/utils.jsx';
import BackstageCategory from './backstage_category.jsx';
import BackstageSection from './backstage_section.jsx';
import {FormattedMessage} from 'react-intl';
@@ -14,7 +15,7 @@ export default class BackstageSidebar extends React.Component {
<ul>
<BackstageCategory
name='integrations'
- parentLink={'/settings'}
+ parentLink={'/' + Utils.getTeamNameFromUrl() + '/settings'}
icon='fa-link'
title={
<FormattedMessage
diff --git a/webapp/components/backstage/installed_commands.jsx b/webapp/components/backstage/installed_commands.jsx
index 3527a574b..8b0cd59c8 100644
--- a/webapp/components/backstage/installed_commands.jsx
+++ b/webapp/components/backstage/installed_commands.jsx
@@ -5,6 +5,7 @@ import React from 'react';
import * as AsyncClient from 'utils/async_client.jsx';
import IntegrationStore from 'stores/integration_store.jsx';
+import * as Utils from 'utils/utils.jsx';
import {FormattedMessage} from 'react-intl';
import InstalledCommand from './installed_command.jsx';
@@ -84,7 +85,7 @@ export default class InstalledCommands extends React.Component {
defaultMessage='Add Slash Command'
/>
}
- addLink='/settings/integrations/commands/add'
+ addLink={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands/add'}
>
{commands}
</InstalledIntegrations>
diff --git a/webapp/components/backstage/installed_incoming_webhook.jsx b/webapp/components/backstage/installed_incoming_webhook.jsx
index 58d318310..afa6e9958 100644
--- a/webapp/components/backstage/installed_incoming_webhook.jsx
+++ b/webapp/components/backstage/installed_incoming_webhook.jsx
@@ -90,6 +90,17 @@ export default class InstalledIncomingWebhook extends React.Component {
</span>
</div>
{description}
+ <div className='item-details__row'>
+ <span className='item-details__url'>
+ <FormattedMessage
+ id='installed_integrations.url'
+ defaultMessage='URL: {url}'
+ values={{
+ url: Utils.getWindowLocationOrigin() + '/hooks/' + incomingWebhook.id
+ }}
+ />
+ </span>
+ </div>
<div className='tem-details__row'>
<span className='item-details__creation'>
<FormattedMessage
diff --git a/webapp/components/backstage/installed_incoming_webhooks.jsx b/webapp/components/backstage/installed_incoming_webhooks.jsx
index de7154afe..0d6f900d1 100644
--- a/webapp/components/backstage/installed_incoming_webhooks.jsx
+++ b/webapp/components/backstage/installed_incoming_webhooks.jsx
@@ -5,6 +5,7 @@ import React from 'react';
import * as AsyncClient from 'utils/async_client.jsx';
import IntegrationStore from 'stores/integration_store.jsx';
+import * as Utils from 'utils/utils.jsx';
import {FormattedMessage} from 'react-intl';
import InstalledIncomingWebhook from './installed_incoming_webhook.jsx';
@@ -76,7 +77,7 @@ export default class InstalledIncomingWebhooks extends React.Component {
defaultMessage='Add Incoming Webhook'
/>
}
- addLink='/settings/integrations/incoming_webhooks/add'
+ addLink={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/incoming_webhooks/add'}
>
{incomingWebhooks}
</InstalledIntegrations>
diff --git a/webapp/components/backstage/installed_outgoing_webhooks.jsx b/webapp/components/backstage/installed_outgoing_webhooks.jsx
index 15d927a41..98992b081 100644
--- a/webapp/components/backstage/installed_outgoing_webhooks.jsx
+++ b/webapp/components/backstage/installed_outgoing_webhooks.jsx
@@ -5,6 +5,7 @@ import React from 'react';
import * as AsyncClient from 'utils/async_client.jsx';
import IntegrationStore from 'stores/integration_store.jsx';
+import * as Utils from 'utils/utils.jsx';
import {FormattedMessage} from 'react-intl';
import InstalledOutgoingWebhook from './installed_outgoing_webhook.jsx';
@@ -82,7 +83,7 @@ export default class InstalledOutgoingWebhooks extends React.Component {
defaultMessage='Add Outgoing Webhook'
/>
}
- addLink='/settings/integrations/outgoing_webhooks/add'
+ addLink={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks/add'}
>
{outgoingWebhooks}
</InstalledIntegrations>
diff --git a/webapp/components/backstage/integrations.jsx b/webapp/components/backstage/integrations.jsx
index 71232ea45..fdd75026a 100644
--- a/webapp/components/backstage/integrations.jsx
+++ b/webapp/components/backstage/integrations.jsx
@@ -5,6 +5,7 @@ import React from 'react';
import {FormattedMessage} from 'react-intl';
import IntegrationOption from './integration_option.jsx';
+import * as Utils from 'utils/utils.jsx';
import WebhookIcon from 'images/webhook_icon.jpg';
@@ -29,7 +30,7 @@ export default class Integrations extends React.Component {
defaultMessage='Incoming webhooks allow external integrations to send messages'
/>
}
- link={'/settings/integrations/incoming_webhooks'}
+ link={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/incoming_webhooks'}
/>
);
}
@@ -51,7 +52,7 @@ export default class Integrations extends React.Component {
defaultMessage='Outgoing webhooks allow external integrations to receive and respond to messages'
/>
}
- link={'/settings/integrations/outgoing_webhooks'}
+ link={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks'}
/>
);
}
@@ -73,7 +74,7 @@ export default class Integrations extends React.Component {
defaultMessage='Slash commands send events to an external integration'
/>
}
- link={'/settings/integrations/commands'}
+ link={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands'}
/>
);
}
diff --git a/webapp/components/channel_header.jsx b/webapp/components/channel_header.jsx
index 16d9ea536..c82f59299 100644
--- a/webapp/components/channel_header.jsx
+++ b/webapp/components/channel_header.jsx
@@ -25,7 +25,7 @@ import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as Utils from 'utils/utils.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
import {FormattedMessage} from 'react-intl';
diff --git a/webapp/components/channel_info_modal.jsx b/webapp/components/channel_info_modal.jsx
index c7f9f9f79..4c16dda90 100644
--- a/webapp/components/channel_info_modal.jsx
+++ b/webapp/components/channel_info_modal.jsx
@@ -3,33 +3,26 @@
import * as Utils from 'utils/utils.jsx';
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-intl';
-
+import {FormattedMessage} from 'react-intl';
import {Modal} from 'react-bootstrap';
-const holders = defineMessages({
- notFound: {
- id: 'channel_info.notFound',
- defaultMessage: 'No Channel Found'
- }
-});
-
import React from 'react';
-class ChannelInfoModal extends React.Component {
+export default class ChannelInfoModal extends React.Component {
render() {
- const {formatMessage} = this.props.intl;
let channel = this.props.channel;
if (!channel) {
+ const notFound = Utils.localizeMessage('channel_info.notFound', 'No Channel Found');
+
channel = {
- display_name: formatMessage(holders.notFound),
- name: formatMessage(holders.notFound),
- purpose: formatMessage(holders.notFound),
- id: formatMessage(holders.notFound)
+ display_name: notFound,
+ name: notFound,
+ purpose: notFound,
+ id: notFound
};
}
- const channelURL = Utils.getShortenedTeamURL() + channel.name;
+ const channelURL = Utils.getTeamURLFromAddressBar() + '/channels/' + channel.name;
return (
<Modal
@@ -97,10 +90,7 @@ class ChannelInfoModal extends React.Component {
}
ChannelInfoModal.propTypes = {
- intl: intlShape.isRequired,
show: React.PropTypes.bool.isRequired,
onHide: React.PropTypes.func.isRequired,
channel: React.PropTypes.object.isRequired
-};
-
-export default injectIntl(ChannelInfoModal); \ No newline at end of file
+}; \ No newline at end of file
diff --git a/webapp/components/channel_invite_button.jsx b/webapp/components/channel_invite_button.jsx
index 1fcd461ea..ed013bb26 100644
--- a/webapp/components/channel_invite_button.jsx
+++ b/webapp/components/channel_invite_button.jsx
@@ -4,7 +4,7 @@
import React from 'react';
import * as AsyncClient from 'utils/async_client.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import {FormattedMessage} from 'react-intl';
import SpinnerButton from 'components/spinner_button.jsx';
@@ -37,13 +37,9 @@ export default class ChannelInviteButton extends React.Component {
addingUser: true
});
- const data = {
- user_id: this.props.user.id
- };
-
Client.addChannelMember(
this.props.channel.id,
- data,
+ this.props.user.id,
() => {
this.setState({
addingUser: false
diff --git a/webapp/components/channel_members_modal.jsx b/webapp/components/channel_members_modal.jsx
index 67be2ef50..ecea891bb 100644
--- a/webapp/components/channel_members_modal.jsx
+++ b/webapp/components/channel_members_modal.jsx
@@ -9,7 +9,7 @@ import UserStore from 'stores/user_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as Utils from 'utils/utils.jsx';
import {FormattedMessage} from 'react-intl';
@@ -97,12 +97,9 @@ export default class ChannelMembersModal extends React.Component {
handleRemove(user) {
const userId = user.id;
- const data = {};
- data.user_id = userId;
-
Client.removeChannelMember(
ChannelStore.getCurrentId(),
- data,
+ userId,
() => {
const memberList = this.state.memberList.slice();
for (let i = 0; i < memberList.length; i++) {
diff --git a/webapp/components/channel_notifications_modal.jsx b/webapp/components/channel_notifications_modal.jsx
index 564776876..112c07ad0 100644
--- a/webapp/components/channel_notifications_modal.jsx
+++ b/webapp/components/channel_notifications_modal.jsx
@@ -6,7 +6,7 @@ import {Modal} from 'react-bootstrap';
import SettingItemMin from './setting_item_min.jsx';
import SettingItemMax from './setting_item_max.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import {FormattedMessage} from 'react-intl';
@@ -60,7 +60,7 @@ export default class ChannelNotificationsModal extends React.Component {
data.desktop = notifyLevel;
//TODO: This should be moved to event_helpers
- Client.updateNotifyProps(data,
+ Client.updateChannelNotifyProps(data,
() => {
// YUCK
var member = ChannelStore.getMember(channelId);
@@ -252,7 +252,7 @@ export default class ChannelNotificationsModal extends React.Component {
};
//TODO: This should be fixed, moved to event_helpers
- Client.updateNotifyProps(data,
+ Client.updateChannelNotifyProps(data,
() => {
// Yuck...
var member = ChannelStore.getMember(channelId);
diff --git a/webapp/components/channel_select.jsx b/webapp/components/channel_select.jsx
index 8622d1f57..238cfa1ae 100644
--- a/webapp/components/channel_select.jsx
+++ b/webapp/components/channel_select.jsx
@@ -54,7 +54,7 @@ export default class ChannelSelect extends React.Component {
];
this.state.channels.forEach((channel) => {
- if (channel.type !== Constants.DM_CHANNEL) {
+ if (channel.type === Constants.OPEN_CHANNEL) {
options.push(
<option
key={channel.id}
diff --git a/webapp/components/claim/claim.jsx b/webapp/components/claim/claim.jsx
index 5cfb04af3..0197e1677 100644
--- a/webapp/components/claim/claim.jsx
+++ b/webapp/components/claim/claim.jsx
@@ -1,8 +1,6 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import TeamStore from 'stores/team_store.jsx';
-
import React from 'react';
import {FormattedMessage} from 'react-intl';
import {Link} from 'react-router';
@@ -13,40 +11,15 @@ export default class Claim extends React.Component {
constructor(props) {
super(props);
- this.onTeamChange = this.onTeamChange.bind(this);
- this.updateStateFromStores = this.updateStateFromStores.bind(this);
-
this.state = {};
}
componentWillMount() {
this.setState({
email: this.props.location.query.email,
newType: this.props.location.query.new_type,
- oldType: this.props.location.query.old_type,
- teamName: this.props.params.team,
- teamDisplayName: ''
- });
- this.updateStateFromStores();
- }
- componentDidMount() {
- TeamStore.addChangeListener(this.onTeamChange);
- }
- componentWillUnmount() {
- TeamStore.removeChangeListener(this.onTeamChange);
- }
- updateStateFromStores() {
- const team = TeamStore.getByName(this.state.teamName);
- let displayName = '';
- if (team) {
- displayName = team.display_name;
- }
- this.setState({
- teamDisplayName: displayName
+ oldType: this.props.location.query.old_type
});
}
- onTeamChange() {
- this.updateStateFromStores();
- }
render() {
return (
<div>
@@ -66,8 +39,6 @@ export default class Claim extends React.Component {
/>
<div id='claim'>
{React.cloneElement(this.props.children, {
- teamName: this.state.teamName,
- teamDisplayName: this.state.teamDisplayName,
currentType: this.state.oldType,
newType: this.state.newType,
email: this.state.email
@@ -83,7 +54,6 @@ export default class Claim extends React.Component {
Claim.defaultProps = {
};
Claim.propTypes = {
- params: React.PropTypes.object.isRequired,
location: React.PropTypes.object.isRequired,
children: React.PropTypes.node
};
diff --git a/webapp/components/claim/components/email_to_ldap.jsx b/webapp/components/claim/components/email_to_ldap.jsx
index 1ceb42a27..fbf26cade 100644
--- a/webapp/components/claim/components/email_to_ldap.jsx
+++ b/webapp/components/claim/components/email_to_ldap.jsx
@@ -2,12 +2,11 @@
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import React from 'react';
import ReactDOM from 'react-dom';
import {FormattedMessage} from 'react-intl';
-import {browserHistory} from 'react-router';
export default class EmailToLDAP extends React.Component {
constructor(props) {
@@ -45,17 +44,14 @@ export default class EmailToLDAP extends React.Component {
state.error = null;
this.setState(state);
- var postData = {};
- postData.email_password = password;
- postData.ldap_id = ldapId;
- postData.ldap_password = ldapPassword;
- postData.email = this.props.email;
- postData.team_name = this.props.teamName;
-
- Client.emailToLDAP(postData,
+ Client.emailToLdap(
+ this.props.email,
+ password,
+ ldapId,
+ ldapPassword,
(data) => {
if (data.follow_link) {
- browserHistory.push(data.follow_link);
+ window.location.href = data.follow_link;
}
},
(error) => {
@@ -74,6 +70,20 @@ export default class EmailToLDAP extends React.Component {
formClass += ' has-error';
}
+ let loginPlaceholder;
+ if (global.window.mm_config.LdapLoginFieldName) {
+ loginPlaceholder = global.window.mm_config.LdapLoginFieldName;
+ } else {
+ loginPlaceholder = Utils.localizeMessage('claim.email_to_ldap.ldapId', 'LDAP ID');
+ }
+
+ let passwordPlaceholder;
+ if (global.window.mm_config.LdapPasswordFieldName) {
+ passwordPlaceholder = global.window.mm_config.LdapPasswordFieldName;
+ } else {
+ passwordPlaceholder = Utils.localizeMessage('claim.email_to_ldap.ldapPwd', 'LDAP Password');
+ }
+
return (
<div>
<h3>
@@ -98,9 +108,8 @@ export default class EmailToLDAP extends React.Component {
<p>
<FormattedMessage
id='claim.email_to_ldap.enterPwd'
- defaultMessage='Enter the password for your {team} {site} email account'
+ defaultMessage='Enter the password for your {site} email account'
values={{
- team: this.props.teamDisplayName,
site: global.window.mm_config.SiteName
}}
/>
@@ -125,10 +134,6 @@ export default class EmailToLDAP extends React.Component {
<FormattedMessage
id='claim.email_to_ldap.enterLdapPwd'
defaultMessage='Enter the ID and password for your LDAP account'
- values={{
- team: this.props.teamDisplayName,
- site: global.window.mm_config.SiteName
- }}
/>
</p>
<div className={formClass}>
@@ -138,7 +143,7 @@ export default class EmailToLDAP extends React.Component {
name='ldapId'
ref='ldapid'
autoComplete='off'
- placeholder={Utils.localizeMessage('claim.email_to_ldap.ldapId', 'LDAP ID')}
+ placeholder={loginPlaceholder}
spellCheck='false'
/>
</div>
@@ -149,7 +154,7 @@ export default class EmailToLDAP extends React.Component {
name='ldapPassword'
ref='ldappassword'
autoComplete='off'
- placeholder={Utils.localizeMessage('claim.email_to_ldap.ldapPwd', 'LDAP Password')}
+ placeholder={passwordPlaceholder}
spellCheck='false'
/>
</div>
@@ -172,7 +177,5 @@ export default class EmailToLDAP extends React.Component {
EmailToLDAP.defaultProps = {
};
EmailToLDAP.propTypes = {
- email: React.PropTypes.string,
- teamName: React.PropTypes.string,
- teamDisplayName: React.PropTypes.string
+ email: React.PropTypes.string
};
diff --git a/webapp/components/claim/components/email_to_oauth.jsx b/webapp/components/claim/components/email_to_oauth.jsx
index f4376067a..1fd284bed 100644
--- a/webapp/components/claim/components/email_to_oauth.jsx
+++ b/webapp/components/claim/components/email_to_oauth.jsx
@@ -2,12 +2,11 @@
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import React from 'react';
import ReactDOM from 'react-dom';
import {FormattedMessage} from 'react-intl';
-import {browserHistory} from 'react-router';
export default class EmailToOAuth extends React.Component {
constructor(props) {
@@ -31,16 +30,13 @@ export default class EmailToOAuth extends React.Component {
state.error = null;
this.setState(state);
- var postData = {};
- postData.password = password;
- postData.email = this.props.email;
- postData.team_name = this.props.teamName;
- postData.service = this.props.newType;
-
- Client.emailToOAuth(postData,
+ Client.emailToOAuth(
+ this.props.email,
+ password,
+ this.props.newType,
(data) => {
if (data.follow_link) {
- browserHistory.push(data.follow_link);
+ window.location.href = data.follow_link;
}
},
(error) => {
@@ -94,9 +90,8 @@ export default class EmailToOAuth extends React.Component {
<p>
<FormattedMessage
id='claim.email_to_oauth.enterPwd'
- defaultMessage='Enter the password for your {team} {site} account'
+ defaultMessage='Enter the password for your {site} account'
values={{
- team: this.props.teamDisplayName,
site: global.window.mm_config.SiteName
}}
/>
@@ -134,7 +129,5 @@ EmailToOAuth.defaultProps = {
};
EmailToOAuth.propTypes = {
newType: React.PropTypes.string,
- email: React.PropTypes.string,
- teamName: React.PropTypes.string,
- teamDisplayName: React.PropTypes.string
+ email: React.PropTypes.string
};
diff --git a/webapp/components/claim/components/ldap_to_email.jsx b/webapp/components/claim/components/ldap_to_email.jsx
index ed8a314bd..a10cefd6f 100644
--- a/webapp/components/claim/components/ldap_to_email.jsx
+++ b/webapp/components/claim/components/ldap_to_email.jsx
@@ -2,12 +2,11 @@
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import React from 'react';
import ReactDOM from 'react-dom';
import {FormattedMessage} from 'react-intl';
-import {browserHistory} from 'react-router';
export default class LDAPToEmail extends React.Component {
constructor(props) {
@@ -45,16 +44,13 @@ export default class LDAPToEmail extends React.Component {
state.error = null;
this.setState(state);
- var postData = {};
- postData.email_password = password;
- postData.ldap_password = ldapPassword;
- postData.email = this.props.email;
- postData.team_name = this.props.teamName;
-
- Client.ldapToEmail(postData,
+ Client.ldapToEmail(
+ this.props.email,
+ password,
+ ldapPassword,
(data) => {
if (data.follow_link) {
- browserHistory.push(data.follow_link);
+ window.location.href = data.follow_link;
}
},
(error) => {
@@ -73,6 +69,13 @@ export default class LDAPToEmail extends React.Component {
formClass += ' has-error';
}
+ let passwordPlaceholder;
+ if (global.window.mm_config.LdapPasswordFieldName) {
+ passwordPlaceholder = global.window.mm_config.LdapPasswordFieldName;
+ } else {
+ passwordPlaceholder = Utils.localizeMessage('claim.ldap_to_email.ldapPwd', 'LDAP Password');
+ }
+
return (
<div>
<h3>
@@ -100,9 +103,9 @@ export default class LDAPToEmail extends React.Component {
<p>
<FormattedMessage
id='claim.ldap_to_email.enterLdapPwd'
- defaultMessage='Enter your LDAP password for your {team} {site} email account'
+ defaultMessage='Enter your {ldapPassword} for your {site} email account'
values={{
- team: this.props.teamDisplayName,
+ ldapPassword: passwordPlaceholder,
site: global.window.mm_config.SiteName
}}
/>
@@ -113,7 +116,7 @@ export default class LDAPToEmail extends React.Component {
className='form-control'
name='ldapPassword'
ref='ldappassword'
- placeholder={Utils.localizeMessage('claim.ldap_to_email.ldapPwd', 'LDAP Password')}
+ placeholder={passwordPlaceholder}
spellCheck='false'
/>
</div>
@@ -162,7 +165,5 @@ export default class LDAPToEmail extends React.Component {
LDAPToEmail.defaultProps = {
};
LDAPToEmail.propTypes = {
- email: React.PropTypes.string,
- teamName: React.PropTypes.string,
- teamDisplayName: React.PropTypes.string
+ email: React.PropTypes.string
};
diff --git a/webapp/components/claim/components/oauth_to_email.jsx b/webapp/components/claim/components/oauth_to_email.jsx
index 72e0500a9..7fd18aaa6 100644
--- a/webapp/components/claim/components/oauth_to_email.jsx
+++ b/webapp/components/claim/components/oauth_to_email.jsx
@@ -2,7 +2,7 @@
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import React from 'react';
import ReactDOM from 'react-dom';
@@ -38,12 +38,9 @@ export default class OAuthToEmail extends React.Component {
state.error = null;
this.setState(state);
- var postData = {};
- postData.password = password;
- postData.email = this.props.email;
- postData.team_name = this.props.teamName;
-
- Client.oauthToEmail(postData,
+ Client.oauthToEmail(
+ this.props.email,
+ password,
(data) => {
if (data.follow_link) {
browserHistory.push(data.follow_link);
@@ -87,10 +84,9 @@ export default class OAuthToEmail extends React.Component {
</p>
<p>
<FormattedMessage
- id='claim.oauth_to_email_newPwd'
- defaultMessage='Enter a new password for your {team} {site} account'
+ id='claim.oauth_to_email.enterNewPwd'
+ defaultMessage='Enter a new password for your {site} account'
values={{
- team: this.props.teamDisplayName,
site: global.window.mm_config.SiteName
}}
/>
@@ -137,8 +133,6 @@ export default class OAuthToEmail extends React.Component {
OAuthToEmail.defaultProps = {
};
OAuthToEmail.propTypes = {
- teamName: React.PropTypes.string,
- teamDisplayName: React.PropTypes.string,
currentType: React.PropTypes.string,
email: React.PropTypes.string
};
diff --git a/webapp/components/create_comment.jsx b/webapp/components/create_comment.jsx
index a91c03d58..e8fa59165 100644
--- a/webapp/components/create_comment.jsx
+++ b/webapp/components/create_comment.jsx
@@ -4,7 +4,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import UserStore from 'stores/user_store.jsx';
@@ -148,7 +148,6 @@ class CreateComment extends React.Component {
Client.createPost(
post,
- ChannelStore.getCurrent(),
(data) => {
AsyncClient.getPosts(this.props.channelId);
diff --git a/webapp/components/create_post.jsx b/webapp/components/create_post.jsx
index 232537d8b..9bbc44f38 100644
--- a/webapp/components/create_post.jsx
+++ b/webapp/components/create_post.jsx
@@ -11,7 +11,7 @@ import TutorialTip from './tutorial/tutorial_tip.jsx';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx';
@@ -172,7 +172,7 @@ class CreatePost extends React.Component {
GlobalActions.emitUserPostedEvent(post);
this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
- Client.createPost(post, channel,
+ Client.createPost(post,
(data) => {
AsyncClient.getPosts();
diff --git a/webapp/components/signup_team_complete/components/team_signup_display_name_page.jsx b/webapp/components/create_team/components/display_name.jsx
index 111fc6835..e33eee1bc 100644
--- a/webapp/components/signup_team_complete/components/team_signup_display_name_page.jsx
+++ b/webapp/components/create_team/components/display_name.jsx
@@ -3,7 +3,8 @@
import ReactDOM from 'react-dom';
import * as utils from 'utils/utils.jsx';
-import * as client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
+import {Link} from 'react-router';
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';
@@ -11,11 +12,11 @@ import logoImage from 'images/logo.png';
const holders = defineMessages({
required: {
- id: 'team_signup_display_name.required',
+ id: 'create_team_display_name.required',
defaultMessage: 'This field is required'
},
charLength: {
- id: 'team_signup_display_name.charLength',
+ id: 'create_team_display_name.charLength',
defaultMessage: 'Name must be 4 or more characters up to a maximum of 15'
}
});
@@ -26,16 +27,11 @@ class TeamSignupDisplayNamePage extends React.Component {
constructor(props) {
super(props);
- this.submitBack = this.submitBack.bind(this);
this.submitNext = this.submitNext.bind(this);
this.state = {};
}
- submitBack(e) {
- e.preventDefault();
- this.props.state.wizard = 'welcome';
- this.props.updateParent(this.props.state);
- }
+
submitNext(e) {
e.preventDefault();
@@ -54,12 +50,14 @@ class TeamSignupDisplayNamePage extends React.Component {
this.props.state.team.name = utils.cleanUpUrlable(displayName);
this.props.updateParent(this.props.state);
}
+
handleFocus(e) {
e.preventDefault();
e.currentTarget.select();
}
+
render() {
- client.track('signup', 'signup_team_02_name');
+ Client.track('signup', 'signup_team_02_name');
var nameError = null;
var nameDivClass = 'form-group';
@@ -77,7 +75,7 @@ class TeamSignupDisplayNamePage extends React.Component {
/>
<h2>
<FormattedMessage
- id='team_signup_display_name.teamName'
+ id='create_team_display_name.teamName'
defaultMessage='Team Name'
/>
</h2>
@@ -101,7 +99,7 @@ class TeamSignupDisplayNamePage extends React.Component {
</div>
<div>
<FormattedMessage
- id='team_signup_display_name.nameHelp'
+ id='create_team_display_name.nameHelp'
defaultMessage='Name your team in any language. Your team name shows in menus and headings.'
/>
</div>
@@ -111,20 +109,17 @@ class TeamSignupDisplayNamePage extends React.Component {
onClick={this.submitNext}
>
<FormattedMessage
- id='team_signup_display_name.next'
+ id='create_team_display_name.next'
defaultMessage='Next'
/><i className='glyphicon glyphicon-chevron-right'></i>
</button>
<div className='margin--extra'>
- <a
- href='#'
- onClick={this.submitBack}
- >
+ <Link to='/select_team'>
<FormattedMessage
- id='team_signup_display_name.back'
+ id='create_team_display_name.back'
defaultMessage='Back to previous step'
/>
- </a>
+ </Link>
</div>
</form>
</div>
diff --git a/webapp/components/signup_team_complete/components/team_signup_url_page.jsx b/webapp/components/create_team/components/team_url.jsx
index b2ab57285..025103141 100644
--- a/webapp/components/signup_team_complete/components/team_signup_url_page.jsx
+++ b/webapp/components/create_team/components/team_url.jsx
@@ -4,8 +4,11 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
+import TeamStore from 'stores/team_store.jsx';
+import UserStore from 'stores/user_store.jsx';
import Constants from 'utils/constants.jsx';
+import {browserHistory} from 'react-router';
import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl';
@@ -13,30 +16,30 @@ import logoImage from 'images/logo.png';
const holders = defineMessages({
required: {
- id: 'team_signup_url.required',
+ id: 'create_team_url.required',
defaultMessage: 'This field is required'
},
regex: {
- id: 'team_signup_url.regex',
+ id: 'create_team_url.regex',
defaultMessage: "Use only lower case letters, numbers and dashes. Must start with a letter and can't end in a dash."
},
charLength: {
- id: 'team_signup_url.charLength',
+ id: 'create_team_url.charLength',
defaultMessage: 'Name must be 4 or more characters up to a maximum of 15'
},
taken: {
- id: 'team_signup_url.taken',
+ id: 'create_team_url.taken',
defaultMessage: 'URL is taken or contains a reserved word'
},
unavailable: {
- id: 'team_signup_url.unavailable',
+ id: 'create_team_url.unavailable',
defaultMessage: 'This URL is unavailable. Please try another.'
}
});
import React from 'react';
-class TeamSignupUrlPage extends React.Component {
+class TeamUrl extends React.Component {
constructor(props) {
super(props);
@@ -48,7 +51,7 @@ class TeamSignupUrlPage extends React.Component {
}
submitBack(e) {
e.preventDefault();
- this.props.state.wizard = 'team_display_name';
+ this.props.state.wizard = 'display_name';
this.props.updateParent(this.props.state);
}
submitNext(e) {
@@ -81,25 +84,39 @@ class TeamSignupUrlPage extends React.Component {
}
}
+ $('#finish-button').button('loading');
+ var teamSignup = JSON.parse(JSON.stringify(this.props.state));
+ teamSignup.team.type = 'O';
+ teamSignup.team.name = name;
+
Client.findTeamByName(name,
- (data) => {
- if (data) {
- this.setState({nameError: formatMessage(holders.unavailable)});
- } else {
- if (global.window.mm_config.SendEmailNotifications === 'true') {
- this.props.state.wizard = 'send_invites';
- } else {
- this.props.state.wizard = 'username';
- }
- this.props.state.team.type = 'O';
-
- this.props.state.team.name = name;
- this.props.updateParent(this.props.state);
- }
- },
- (err) => {
- this.setState({nameError: err.message});
- }
+ (findTeam) => {
+ if (findTeam) {
+ this.setState({nameError: formatMessage(holders.unavailable)});
+ $('#finish-button').button('reset');
+ } else {
+ Client.createTeam(teamSignup.team,
+ (team) => {
+ Client.track('signup', 'signup_team_08_complete');
+ $('#sign-up-button').button('reset');
+ TeamStore.saveTeam(team);
+ TeamStore.appendTeamMember({team_id: team.id, user_id: UserStore.getCurrentId(), roles: 'admin'});
+ TeamStore.emitChange();
+ browserHistory.push('/' + team.name + '/channels/town-square');
+ },
+ (err) => {
+ this.setState({nameError: err.message});
+ $('#finish-button').button('reset');
+ }
+ );
+
+ $('#finish-button').button('reset');
+ }
+ },
+ (err) => {
+ this.setState({nameError: err.message});
+ $('#finish-button').button('reset');
+ }
);
}
handleFocus(e) {
@@ -130,7 +147,7 @@ class TeamSignupUrlPage extends React.Component {
/>
<h2>
<FormattedMessage
- id='team_signup_url.teamUrl'
+ id='create_team_url.teamUrl'
defaultMessage='Team URL'
/>
</h2>
@@ -163,13 +180,13 @@ class TeamSignupUrlPage extends React.Component {
</div>
<p>
<FormattedMessage
- id='team_signup_url.webAddress'
+ id='create_team_url.webAddress'
defaultMessage='Choose the web address of your new team:'
/>
</p>
<ul className='color--light'>
<FormattedHTMLMessage
- id='team_signup_url.hint'
+ id='create_team_url.hint'
defaultMessage="<li>Short and memorable is best</li>
<li>Use lowercase letters, numbers and dashes</li>
<li>Must start with a letter and can't end in a dash</li>"
@@ -177,13 +194,14 @@ class TeamSignupUrlPage extends React.Component {
</ul>
<button
type='submit'
+ id='finish-button'
className='btn btn-primary margin--extra'
onClick={this.submitNext}
>
<FormattedMessage
- id='team_signup_url.next'
- defaultMessage='Next'
- /><i className='glyphicon glyphicon-chevron-right'></i>
+ id='create_team_password.finish'
+ defaultMessage='Finish'
+ />
</button>
<div className='margin--extra'>
<a
@@ -191,7 +209,7 @@ class TeamSignupUrlPage extends React.Component {
onClick={this.submitBack}
>
<FormattedMessage
- id='team_signup_url.back'
+ id='create_team_url.back'
defaultMessage='Back to previous step'
/>
</a>
@@ -202,10 +220,10 @@ class TeamSignupUrlPage extends React.Component {
}
}
-TeamSignupUrlPage.propTypes = {
+TeamUrl.propTypes = {
intl: intlShape.isRequired,
state: React.PropTypes.object,
updateParent: React.PropTypes.func
};
-export default injectIntl(TeamSignupUrlPage);
+export default injectIntl(TeamUrl);
diff --git a/webapp/components/create_team/create_team.jsx b/webapp/components/create_team/create_team.jsx
new file mode 100644
index 000000000..8a119a122
--- /dev/null
+++ b/webapp/components/create_team/create_team.jsx
@@ -0,0 +1,72 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import ErrorBar from 'components/error_bar.jsx';
+
+import {FormattedMessage} from 'react-intl';
+import {browserHistory, Link} from 'react-router';
+
+import React from 'react';
+
+export default class CreateTeam extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.submit = this.submit.bind(this);
+ this.updateParent = this.updateParent.bind(this);
+
+ const state = {};
+ state.team = {};
+ state.wizard = 'display_name';
+ this.state = state;
+ }
+
+ submit() {
+ // todo fill in
+ }
+
+ componentDidMount() {
+ browserHistory.push('/create_team/display_name');
+ }
+
+ updateParent(state) {
+ this.setState(state);
+ browserHistory.push('/create_team/' + state.wizard);
+ }
+
+ render() {
+ return (
+ <div>
+ <ErrorBar/>
+ <div className='signup-header'>
+ <Link to='/select_team'>
+ <span className='fa fa-chevron-left'/>
+ <FormattedMessage
+ id='web.header.back'
+ />
+ </Link>
+ </div>
+ <div className='col-sm-12'>
+ <div className='signup-team__container'>
+ <h1>{global.window.mm_config.SiteName}</h1>
+ <h4 className='color--light'>
+ <FormattedMessage
+ id='web.root.singup_info'
+ />
+ </h4>
+ <div className='signup__content'>
+ {React.cloneElement(this.props.children, {
+ state: this.state,
+ updateParent: this.updateParent
+ })}
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+}
+
+CreateTeam.propTypes = {
+ children: React.PropTypes.node
+};
diff --git a/webapp/components/delete_channel_modal.jsx b/webapp/components/delete_channel_modal.jsx
index 244472a56..222b72c41 100644
--- a/webapp/components/delete_channel_modal.jsx
+++ b/webapp/components/delete_channel_modal.jsx
@@ -2,7 +2,7 @@
// See License.txt for license information.
import * as AsyncClient from 'utils/async_client.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import {Modal} from 'react-bootstrap';
import TeamStore from 'stores/team_store.jsx';
import Constants from 'utils/constants.jsx';
diff --git a/webapp/components/delete_post_modal.jsx b/webapp/components/delete_post_modal.jsx
index 0dbdc2b43..b2aea6590 100644
--- a/webapp/components/delete_post_modal.jsx
+++ b/webapp/components/delete_post_modal.jsx
@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import PostStore from 'stores/post_store.jsx';
import ModalStore from 'stores/modal_store.jsx';
import {Modal} from 'react-bootstrap';
diff --git a/webapp/components/do_verify_email.jsx b/webapp/components/do_verify_email.jsx
index c3be111ed..fe4d61a72 100644
--- a/webapp/components/do_verify_email.jsx
+++ b/webapp/components/do_verify_email.jsx
@@ -2,7 +2,7 @@
// See License.txt for license information.
import {FormattedMessage} from 'react-intl';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import LoadingScreen from './loading_screen.jsx';
import {browserHistory, Link} from 'react-router';
@@ -25,14 +25,14 @@ export default class DoVerifyEmail extends React.Component {
const email = this.props.location.query.email;
Client.verifyEmail(
+ uid,
+ hid,
() => {
browserHistory.push('/' + teamName + '/login?extra=verified&email=' + email);
},
(err) => {
this.setState({verifyStatus: 'failure', serverError: err.message});
- },
- uid,
- hid
+ }
);
}
render() {
diff --git a/webapp/components/edit_channel_header_modal.jsx b/webapp/components/edit_channel_header_modal.jsx
index 35a5fb9dc..6a7cccebb 100644
--- a/webapp/components/edit_channel_header_modal.jsx
+++ b/webapp/components/edit_channel_header_modal.jsx
@@ -3,7 +3,7 @@
import ReactDOM from 'react-dom';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
import * as Utils from 'utils/utils.jsx';
diff --git a/webapp/components/edit_channel_purpose_modal.jsx b/webapp/components/edit_channel_purpose_modal.jsx
index 31cbdd240..a4779d022 100644
--- a/webapp/components/edit_channel_purpose_modal.jsx
+++ b/webapp/components/edit_channel_purpose_modal.jsx
@@ -4,7 +4,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as AsyncClient from 'utils/async_client.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-intl';
@@ -49,12 +49,9 @@ export default class EditChannelPurposeModal extends React.Component {
return;
}
- const data = {
- channel_id: this.props.channel.id,
- channel_purpose: ReactDOM.findDOMNode(this.refs.purpose).value.trim()
- };
-
- Client.updateChannelPurpose(data,
+ Client.updateChannelPurpose(
+ this.props.channel.id,
+ ReactDOM.findDOMNode(this.refs.purpose).value.trim(),
() => {
AsyncClient.getChannel(this.props.channel.id);
diff --git a/webapp/components/edit_post_modal.jsx b/webapp/components/edit_post_modal.jsx
index caf9a0ee5..bc67a34f9 100644
--- a/webapp/components/edit_post_modal.jsx
+++ b/webapp/components/edit_post_modal.jsx
@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
import Textbox from './textbox.jsx';
@@ -33,20 +33,25 @@ class EditPostModal extends React.Component {
this.handleEdit = this.handleEdit.bind(this);
this.handleEditInput = this.handleEditInput.bind(this);
this.handleEditKeyPress = this.handleEditKeyPress.bind(this);
- this.handleUserInput = this.handleUserInput.bind(this);
this.handleEditPostEvent = this.handleEditPostEvent.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.onPreferenceChange = this.onPreferenceChange.bind(this);
- this.state = {editText: '', title: '', post_id: '', channel_id: '', comments: 0, refocusId: ''};
+ this.state = {editText: '', originalText: '', title: '', post_id: '', channel_id: '', comments: 0, refocusId: ''};
}
handleEdit() {
var updatedPost = {};
updatedPost.message = this.state.editText.trim();
+ if (updatedPost.message === this.state.originalText.trim()) {
+ // no changes so just close the modal
+ $('#edit_post').modal('hide');
+ return;
+ }
+
if (updatedPost.message.length === 0) {
var tempState = this.state;
- delete tempState.editText;
+ Reflect.deleteProperty(tempState, 'editText');
BrowserStore.setItem('edit_state_transfer', tempState);
$('#edit_post').modal('hide');
GlobalActions.showDeletePostModal(PostStore.getPost(this.state.channel_id, this.state.post_id), this.state.comments);
@@ -79,12 +84,10 @@ class EditPostModal extends React.Component {
this.handleEdit(e);
}
}
- handleUserInput(e) {
- this.setState({editText: e.target.value});
- }
handleEditPostEvent(options) {
this.setState({
editText: options.message || '',
+ originalText: options.message || '',
title: options.title || '',
post_id: options.postId || '',
channel_id: options.channelId || '',
@@ -108,7 +111,7 @@ class EditPostModal extends React.Component {
var self = this;
$(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', () => {
- self.setState({editText: '', title: '', channel_id: '', post_id: '', comments: 0, refocusId: '', error: ''});
+ self.setState({editText: '', originalText: '', title: '', channel_id: '', post_id: '', comments: 0, refocusId: '', error: ''});
});
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', (e) => {
@@ -116,7 +119,15 @@ class EditPostModal extends React.Component {
if (!button) {
return;
}
- self.setState({editText: $(button).attr('data-message'), title: $(button).attr('data-title'), channel_id: $(button).attr('data-channelid'), post_id: $(button).attr('data-postid'), comments: $(button).attr('data-comments'), refocusId: $(button).attr('data-refocusid')});
+ self.setState({
+ editText: $(button).attr('data-message'),
+ originalText: $(button).attr('data-message'),
+ title: $(button).attr('data-title'),
+ channel_id: $(button).attr('data-channelid'),
+ post_id: $(button).attr('data-postid'),
+ comments: $(button).attr('data-comments'),
+ refocusId: $(button).attr('data-refocusid')
+ });
});
$(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', () => {
@@ -163,17 +174,17 @@ class EditPostModal extends React.Component {
aria-label='Close'
onClick={this.handleEditClose}
>
- <span aria-hidden='true'>&times;</span>
+ <span aria-hidden='true'>{'×'}</span>
</button>
- <h4 className='modal-title'>
- <FormattedMessage
- id='edit_post.edit'
- defaultMessage='Edit {title}'
- values={{
- title: this.state.title
- }}
- />
- </h4>
+ <h4 className='modal-title'>
+ <FormattedMessage
+ id='edit_post.edit'
+ defaultMessage='Edit {title}'
+ values={{
+ title: this.state.title
+ }}
+ />
+ </h4>
</div>
<div className='edit-modal-body modal-body'>
<Textbox
diff --git a/webapp/components/error_bar.jsx b/webapp/components/error_bar.jsx
index 572f96e02..7257ffe94 100644
--- a/webapp/components/error_bar.jsx
+++ b/webapp/components/error_bar.jsx
@@ -29,8 +29,8 @@ export default class ErrorBar extends React.Component {
}
componentWillMount() {
- if (global.window.mm_config.SendEmailNotifications === 'false') {
- ErrorStore.storeLastError({message: Utils.localizeMessage('error_bar.preview_mode', 'Preview Mode: Email notifications have not been configured')});
+ if (!ErrorStore.getIgnoreEmailPreview() && global.window.mm_config.SendEmailNotifications === 'false') {
+ ErrorStore.storeLastError({email_preview: true, message: Utils.localizeMessage('error_bar.preview_mode', 'Preview Mode: Email notifications have not been configured')});
this.onErrorChange();
}
}
diff --git a/webapp/components/file_attachment.jsx b/webapp/components/file_attachment.jsx
index ccd4070aa..4a040a35b 100644
--- a/webapp/components/file_attachment.jsx
+++ b/webapp/components/file_attachment.jsx
@@ -4,7 +4,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as utils from 'utils/utils.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
import {intlShape, injectIntl, defineMessages} from 'react-intl';
@@ -100,7 +100,7 @@ class FileAttachment extends React.Component {
getFileInfoFromName(name) {
var fileInfo = utils.splitFileLocation(name);
- fileInfo.path = utils.getWindowLocationOrigin() + '/api/v1/files/get' + fileInfo.path;
+ fileInfo.path = utils.getWindowLocationOrigin() + Client.getFilesRoute() + '/get' + fileInfo.path;
return fileInfo;
}
diff --git a/webapp/components/file_upload.jsx b/webapp/components/file_upload.jsx
index 8d2ec5ac8..03a8a8128 100644
--- a/webapp/components/file_upload.jsx
+++ b/webapp/components/file_upload.jsx
@@ -4,7 +4,7 @@
import $ from 'jquery';
import 'jquery-dragster/jquery.dragster.js';
import ReactDOM from 'react-dom';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import * as Utils from 'utils/utils.jsx';
@@ -49,9 +49,9 @@ class FileUpload extends React.Component {
fileUploadSuccess(channelId, data) {
this.props.onFileUpload(data.filenames, data.client_ids, channelId);
- var requests = this.state.requests;
+ const requests = JSON.parse(JSON.stringify(this.state.requests));
for (var j = 0; j < data.client_ids.length; j++) {
- delete requests[data.client_ids[j]];
+ Reflect.deleteProperty(requests, data.client_ids[j]);
}
this.setState({requests});
}
@@ -64,13 +64,13 @@ class FileUpload extends React.Component {
// clear any existing errors
this.props.onUploadError(null);
- var channelId = this.props.channelId || ChannelStore.getCurrentId();
+ const channelId = this.props.channelId || ChannelStore.getCurrentId();
- var uploadsRemaining = Constants.MAX_UPLOAD_FILES - this.props.getFileCount(channelId);
- var numUploads = 0;
+ const uploadsRemaining = Constants.MAX_UPLOAD_FILES - this.props.getFileCount(channelId);
+ let numUploads = 0;
// keep track of how many files have been too large
- var tooLargeFiles = [];
+ const tooLargeFiles = [];
for (let i = 0; i < files.length && numUploads < uploadsRemaining; i++) {
if (files[i].size > Constants.MAX_FILE_SIZE) {
@@ -81,18 +81,15 @@ class FileUpload extends React.Component {
// generate a unique id that can be used by other components to refer back to this upload
const clientId = Utils.generateId();
- // prepare data to be uploaded
- var formData = new FormData();
- formData.append('channel_id', channelId);
- formData.append('files', files[i], files[i].name);
- formData.append('client_ids', clientId);
-
- var request = Client.uploadFile(formData,
+ const request = Client.uploadFile(files[i],
+ files[i].name,
+ channelId,
+ clientId,
this.fileUploadSuccess.bind(this, channelId),
this.fileUploadFail.bind(this, clientId)
);
- var requests = this.state.requests;
+ const requests = this.state.requests;
requests[clientId] = request;
this.setState({requests});
@@ -231,8 +228,6 @@ class FileUpload extends React.Component {
// generate a unique id that can be used by other components to refer back to this file upload
var clientId = Utils.generateId();
- var formData = new FormData();
- formData.append('channel_id', channelId);
var d = new Date();
var hour;
if (d.getHours() < 10) {
@@ -247,16 +242,17 @@ class FileUpload extends React.Component {
min = String(d.getMinutes());
}
- var name = formatMessage(holders.pasted) + d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate() + ' ' + hour + '-' + min + '.' + ext;
- formData.append('files', file, name);
- formData.append('client_ids', clientId);
+ const name = formatMessage(holders.pasted) + d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate() + ' ' + hour + '-' + min + '.' + ext;
- var request = Client.uploadFile(formData,
+ const request = Client.uploadFile(file,
+ name,
+ channelId,
+ clientId,
self.fileUploadSuccess.bind(self, channelId),
self.fileUploadFail.bind(self, clientId)
);
- var requests = self.state.requests;
+ const requests = self.state.requests;
requests[clientId] = request;
self.setState({requests});
@@ -280,13 +276,13 @@ class FileUpload extends React.Component {
}
cancelUpload(clientId) {
- var requests = this.state.requests;
- var request = requests[clientId];
+ const requests = JSON.parse(JSON.stringify(this.state.requests));
+ const request = requests[clientId];
if (request) {
request.abort();
- delete requests[clientId];
+ Reflect.deleteProperty(requests, clientId);
this.setState({requests});
}
}
diff --git a/webapp/components/filtered_user_list.jsx b/webapp/components/filtered_user_list.jsx
index bd6c49714..5aa0bd96c 100644
--- a/webapp/components/filtered_user_list.jsx
+++ b/webapp/components/filtered_user_list.jsx
@@ -113,6 +113,7 @@ class FilteredUserList extends React.Component {
>
<UserList
users={users}
+ teamMembers={this.props.teamMembers}
actions={this.props.actions}
actionProps={this.props.actionProps}
/>
@@ -124,6 +125,7 @@ class FilteredUserList extends React.Component {
FilteredUserList.defaultProps = {
users: [],
+ teamMembers: [],
actions: [],
actionProps: {}
};
@@ -131,6 +133,7 @@ FilteredUserList.defaultProps = {
FilteredUserList.propTypes = {
intl: intlShape.isRequired,
users: React.PropTypes.arrayOf(React.PropTypes.object),
+ teamMembers: React.PropTypes.arrayOf(React.PropTypes.object),
actions: React.PropTypes.arrayOf(React.PropTypes.func),
actionProps: React.PropTypes.object,
style: React.PropTypes.object
diff --git a/webapp/components/not_logged_in.jsx b/webapp/components/header_footer_template.jsx
index 4beee6259..ce2f59541 100644
--- a/webapp/components/not_logged_in.jsx
+++ b/webapp/components/header_footer_template.jsx
@@ -9,12 +9,12 @@ import {Link} from 'react-router';
export default class NotLoggedIn extends React.Component {
componentDidMount() {
- $('body').attr('class', 'sticky');
- $('#root').attr('class', 'container-fluid');
+ $('body').addClass('sticky');
+ $('#root').addClass('container-fluid');
}
componentWillUnmount() {
- $('body').attr('class', '');
- $('#root').attr('class', '');
+ $('body').removeClass('sticky');
+ $('#root').removeClass('container-fluid');
}
render() {
return (
diff --git a/webapp/components/invite_member_modal.jsx b/webapp/components/invite_member_modal.jsx
index 17cec7aad..4ac620f08 100644
--- a/webapp/components/invite_member_modal.jsx
+++ b/webapp/components/invite_member_modal.jsx
@@ -5,7 +5,7 @@ import ReactDOM from 'react-dom';
import * as utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
import ModalStore from 'stores/modal_store.jsx';
import UserStore from 'stores/user_store.jsx';
diff --git a/webapp/components/logged_in.jsx b/webapp/components/logged_in.jsx
index 1dcb6b0aa..3941dd12c 100644
--- a/webapp/components/logged_in.jsx
+++ b/webapp/components/logged_in.jsx
@@ -2,39 +2,17 @@
// See License.txt for license information.
import $ from 'jquery';
+import LoadingScreen from 'components/loading_screen.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
-import * as GlobalActions from 'action_creators/global_actions.jsx';
import UserStore from 'stores/user_store.jsx';
-import ChannelStore from 'stores/channel_store.jsx';
import BrowserStore from 'stores/browser_store.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
import * as Utils from 'utils/utils.jsx';
-import Constants from 'utils/constants.jsx';
-const TutorialSteps = Constants.TutorialSteps;
-const Preferences = Constants.Preferences;
-import ErrorBar from 'components/error_bar.jsx';
import * as Websockets from 'action_creators/websocket_actions.jsx';
-import LoadingScreen from 'components/loading_screen.jsx';
+import Constants from 'utils/constants.jsx';
import {browserHistory} from 'react-router';
-import SidebarRight from 'components/sidebar_right.jsx';
-import SidebarRightMenu from 'components/sidebar_right_menu.jsx';
-import Navbar from 'components/navbar.jsx';
-
-// Modals
-import GetPostLinkModal from 'components/get_post_link_modal.jsx';
-import GetTeamInviteLinkModal from 'components/get_team_invite_link_modal.jsx';
-import EditPostModal from 'components/edit_post_modal.jsx';
-import DeletePostModal from 'components/delete_post_modal.jsx';
-import MoreChannelsModal from 'components/more_channels.jsx';
-import TeamSettingsModal from 'components/team_settings_modal.jsx';
-import RemovedFromChannelModal from 'components/removed_from_channel_modal.jsx';
-import RegisterAppModal from 'components/register_app_modal.jsx';
-import ImportThemeModal from 'components/user_settings/import_theme_modal.jsx';
-import InviteMemberModal from 'components/invite_member_modal.jsx';
-import SelectTeamModal from 'components/admin_console/select_team_modal.jsx';
-
const CLIENT_STATUS_INTERVAL = 30000;
const BACKSPACE_CHAR = 8;
@@ -45,19 +23,22 @@ export default class LoggedIn extends React.Component {
super(params);
this.onUserChanged = this.onUserChanged.bind(this);
+ this.setupUser = this.setupUser.bind(this);
this.state = {
- user: null,
- profiles: null
+ user: UserStore.getCurrentUser()
};
+
+ if (this.state.user) {
+ this.setupUser(this.state.user);
+ }
}
+
isValidState() {
- return this.state.user != null && this.state.profiles != null;
+ return this.state.user != null;
}
- onUserChanged() {
- // Grab the current user
- const user = UserStore.getCurrentUser();
+ setupUser(user) {
// Update segment indentify
if (global.window.mm_config.SegmentDeveloperKey != null && global.window.mm_config.SegmentDeveloperKey !== '') {
global.window.analytics.identify(user.id, {
@@ -65,7 +46,6 @@ export default class LoggedIn extends React.Component {
email: user.email,
createdAt: user.create_at,
username: user.username,
- team_id: user.team_id,
id: user.id
});
}
@@ -78,25 +58,19 @@ export default class LoggedIn extends React.Component {
Utils.applyTheme(Constants.THEMES.default);
}
}
+ }
- // Go to tutorial if we are first arrivign
- const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
- if (tutorialStep <= TutorialSteps.INTRO_SCREENS) {
- browserHistory.push(Utils.getTeamURLFromAddressBar() + '/tutorial');
- }
-
- // Get profiles
- const profiles = UserStore.getProfiles();
+ onUserChanged() {
+ // Grab the current user
+ const user = UserStore.getCurrentUser();
+ this.setupUser(user);
this.setState({
- user,
- profiles
+ user
});
}
- componentWillMount() {
- // Emit view action
- GlobalActions.viewLoggedIn();
+ componentWillMount() {
// Listen for user
UserStore.addChangeListener(this.onUserChanged);
@@ -116,7 +90,7 @@ export default class LoggedIn extends React.Component {
}
console.log('detected logout from a different tab'); //eslint-disable-line no-console
- browserHistory.push('/' + this.props.params.team);
+ browserHistory.push('/');
}
if (e.originalEvent.key === '__login__' && e.originalEvent.storageArea === localStorage && e.originalEvent.newValue) {
@@ -170,18 +144,6 @@ export default class LoggedIn extends React.Component {
$('body').addClass('ios');
}
- // Set up tracking for whether the window is active
- window.isActive = true;
- $(window).on('focus', () => {
- AsyncClient.updateLastViewedAt();
- ChannelStore.resetCounts(ChannelStore.getCurrentId());
- ChannelStore.emitChange();
- window.isActive = true;
- });
- $(window).on('blur', () => {
- window.isActive = false;
- });
-
// if preferences have already been stored in local storage do not wait until preference store change is fired and handled in channel.jsx
const selectedFont = PreferenceStore.get(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'selected_font', Constants.DEFAULT_FONT);
Utils.applyFont(selectedFont);
@@ -193,13 +155,11 @@ export default class LoggedIn extends React.Component {
}
});
}
+
componentWillUnmount() {
$('#root').attr('class', '');
clearInterval(this.intervalId);
- $(window).off('focus');
- $(window).off('blur');
-
Websockets.close();
UserStore.removeChangeListener(this.onUserChanged);
@@ -211,75 +171,18 @@ export default class LoggedIn extends React.Component {
$(window).off('keydown.preventBackspace');
}
+
render() {
if (!this.isValidState()) {
return <LoadingScreen/>;
}
- let content = [];
- if (this.props.children) {
- content = this.props.children;
- } else {
- content.push(
- this.props.navbar
- );
- content.push(
- this.props.sidebar
- );
- content.push(
- <div
- key='inner-wrap'
- className='inner-wrap channel__wrap'
- >
- <div className='row header'>
- <div id='navbar'>
- <Navbar/>
- </div>
- </div>
- <div className='row main'>
- {React.cloneElement(this.props.center, {
- user: this.state.user,
- profiles: this.state.profiles
- })}
- </div>
- </div>
- );
- }
- return (
- <div className='channel-view'>
- <ErrorBar/>
- <div className='container-fluid'>
- <SidebarRight/>
- <SidebarRightMenu/>
- {content}
-
- <GetPostLinkModal/>
- <GetTeamInviteLinkModal/>
- <InviteMemberModal/>
- <ImportThemeModal/>
- <TeamSettingsModal/>
- <MoreChannelsModal/>
- <EditPostModal/>
- <DeletePostModal/>
- <RemovedFromChannelModal/>
- <RegisterAppModal/>
- <SelectTeamModal/>
- </div>
- </div>
- );
+ return React.cloneElement(this.props.children, {
+ user: this.state.user
+ });
}
}
-LoggedIn.defaultProps = {
-};
-
LoggedIn.propTypes = {
- children: React.PropTypes.oneOfType([
- React.PropTypes.arrayOf(React.PropTypes.element),
- React.PropTypes.element
- ]),
- navbar: React.PropTypes.element,
- sidebar: React.PropTypes.element,
- center: React.PropTypes.element,
- params: React.PropTypes.object
+ children: React.PropTypes.object
};
diff --git a/webapp/components/login/login.jsx b/webapp/components/login/login.jsx
index a3dadbf36..c5955a1f4 100644
--- a/webapp/components/login/login.jsx
+++ b/webapp/components/login/login.jsx
@@ -5,12 +5,14 @@ import LoginEmail from './components/login_email.jsx';
import LoginUsername from './components/login_username.jsx';
import LoginLdap from './components/login_ldap.jsx';
import LoginMfa from './components/login_mfa.jsx';
+import ErrorBar from 'components/error_bar.jsx';
-import TeamStore from 'stores/team_store.jsx';
+import * as GlobalActions from '../../action_creators/global_actions.jsx';
import UserStore from 'stores/user_store.jsx';
+import Client from 'utils/web_client.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
-import * as Client from 'utils/client.jsx';
+
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
@@ -18,40 +20,24 @@ import {FormattedMessage} from 'react-intl';
import {browserHistory, Link} from 'react-router';
import React from 'react';
+import logoImage from 'images/logo.png';
export default class Login extends React.Component {
constructor(props) {
super(props);
- this.getStateFromStores = this.getStateFromStores.bind(this);
- this.onTeamChange = this.onTeamChange.bind(this);
this.preSubmit = this.preSubmit.bind(this);
this.submit = this.submit.bind(this);
+ this.finishSignin = this.finishSignin.bind(this);
- const state = this.getStateFromStores();
- state.doneCheckLogin = false;
+ const state = {};
+ state.showMfa = false;
this.state = state;
}
componentDidMount() {
- TeamStore.addChangeListener(this.onTeamChange);
- Client.getMeLoggedIn((data) => {
- if (data && data.logged_in !== 'false') {
- browserHistory.push('/' + this.props.params.team + '/channels/town-square');
- } else {
- this.setState({doneCheckLogin: true}); //eslint-disable-line react/no-did-mount-set-state
- }
- });
- }
- componentWillUnmount() {
- TeamStore.removeChangeListener(this.onTeamChange);
- }
- getStateFromStores() {
- return {
- currentTeam: TeamStore.getByName(this.props.params.team)
- };
- }
- onTeamChange() {
- this.setState(this.getStateFromStores());
+ if (UserStore.getCurrentUser()) {
+ browserHistory.push('/select_team');
+ }
}
preSubmit(method, loginId, password) {
if (global.window.mm_config.EnableMultifactorAuthentication !== 'true') {
@@ -59,7 +45,7 @@ export default class Login extends React.Component {
return;
}
- Client.checkMfa(method, this.state.currentTeam.name, loginId,
+ Client.checkMfa(method, loginId,
(data) => {
if (data.mfa_required === 'true') {
this.setState({showMfa: true, method, loginId, password});
@@ -78,27 +64,41 @@ export default class Login extends React.Component {
}
);
}
+ finishSignin() {
+ GlobalActions.emitInitialLoad(
+ () => {
+ browserHistory.push('/select_team');
+ }
+ );
+ }
+
submit(method, loginId, password, token) {
this.setState({showMfa: false, serverEmailError: null, serverUsernameError: null, serverLdapError: null});
- const team = this.state.currentTeam.name;
-
if (method === Constants.EMAIL_SERVICE) {
- Client.loginByEmail(team, loginId, password, token,
+ Client.webLogin(
+ loginId,
+ null,
+ password,
+ token,
() => {
UserStore.setLastEmail(loginId);
- browserHistory.push('/' + team + '/channels/town-square');
+ this.finishSignin();
},
(err) => {
if (err.id === 'api.user.login.not_verified.app_error') {
- browserHistory.push('/should_verify_email?teamname=' + encodeURIComponent(team) + '&email=' + encodeURIComponent(loginId));
+ browserHistory.push('/should_verify_email?&email=' + encodeURIComponent(loginId));
return;
}
this.setState({serverEmailError: err.message});
}
);
} else if (method === Constants.USERNAME_SERVICE) {
- Client.loginByUsername(team, loginId, password, token,
+ Client.webLogin(
+ null,
+ loginId,
+ password,
+ token,
() => {
UserStore.setLastUsername(loginId);
@@ -106,7 +106,7 @@ export default class Login extends React.Component {
if (redirect) {
browserHistory.push(decodeURIComponent(redirect));
} else {
- browserHistory.push('/' + team + '/channels/town-square');
+ this.finishSignin();
}
},
(err) => {
@@ -120,13 +120,16 @@ export default class Login extends React.Component {
}
);
} else if (method === Constants.LDAP_SERVICE) {
- Client.loginByLdap(team, loginId, password, token,
+ Client.loginByLdap(
+ loginId,
+ password,
+ token,
() => {
const redirect = Utils.getUrlParameter('redirect');
if (redirect) {
browserHistory.push(decodeURIComponent(redirect));
} else {
- browserHistory.push('/' + team + '/channels/town-square');
+ this.finishSignin();
}
},
(err) => {
@@ -144,7 +147,7 @@ export default class Login extends React.Component {
return (
<div>
<img
- src='/api/v1/admin/get_brand_image'
+ src={Client.getAdminRoute() + '/get_brand_image'}
/>
<p dangerouslySetInnerHTML={{__html: TextFormatting.formatText(text)}}/>
</div>
@@ -153,7 +156,7 @@ export default class Login extends React.Component {
return null;
}
- createLoginOptions(currentTeam) {
+ createLoginOptions() {
const extraParam = Utils.getUrlParameter('extra');
let extraBox = '';
if (extraParam) {
@@ -187,10 +190,19 @@ export default class Login extends React.Component {
/>
</div>
);
+ } else if (extraParam === Constants.PASSWORD_CHANGE) {
+ extraBox = (
+ <div className='alert alert-success'>
+ <i className='fa fa-check'/>
+ <FormattedMessage
+ id='login.passwordChanged'
+ defaultMessage=' Password updated successfully'
+ />
+ </div>
+ );
}
}
- const teamName = currentTeam.name;
const ldapEnabled = global.window.mm_config.EnableLdap === 'true';
const gitlabSigninEnabled = global.window.mm_config.EnableSignUpWithGitLab === 'true';
const googleSigninEnabled = global.window.mm_config.EnableSignUpWithGoogle === 'true';
@@ -200,10 +212,10 @@ export default class Login extends React.Component {
const oauthLogins = [];
if (gitlabSigninEnabled) {
oauthLogins.push(
- <Link
+ <a
className='btn btn-custom-login gitlab'
key='gitlab'
- to={'/api/v1/oauth/gitlab/login?team=' + encodeURIComponent(teamName)}
+ href={Client.getOAuthRoute() + '/gitlab/login'}
>
<span className='icon'/>
<span>
@@ -212,7 +224,7 @@ export default class Login extends React.Component {
defaultMessage='with GitLab'
/>
</span>
- </Link>
+ </a>
);
}
@@ -221,7 +233,7 @@ export default class Login extends React.Component {
<Link
className='btn btn-custom-login google'
key='google'
- to={'/api/v1/oauth/google/login?team=' + encodeURIComponent(teamName)}
+ to={Client.getOAuthRoute() + '/google/login'}
>
<span className='icon'/>
<span>
@@ -238,7 +250,6 @@ export default class Login extends React.Component {
if (emailSigninEnabled) {
emailLogin = (
<LoginEmail
- teamName={teamName}
serverError={this.state.serverEmailError}
submit={this.preSubmit}
/>
@@ -263,7 +274,6 @@ export default class Login extends React.Component {
if (usernameSigninEnabled) {
usernameLogin = (
<LoginUsername
- teamName={teamName}
serverError={this.state.serverUsernameError}
submit={this.preSubmit}
/>
@@ -288,7 +298,6 @@ export default class Login extends React.Component {
if (ldapEnabled) {
ldapLogin = (
<LoginLdap
- teamName={teamName}
serverError={this.state.serverLdapError}
submit={this.preSubmit}
/>
@@ -309,34 +318,31 @@ export default class Login extends React.Component {
}
}
- let userSignUp;
- if (currentTeam.allow_open_invite) {
- userSignUp = (
- <div>
- <span>
+ const userSignUp = (
+ <div>
+ <span>
+ <FormattedMessage
+ id='login.noAccount'
+ defaultMessage="Don't have an account? "
+ />
+ <Link
+ to={'/signup_user_complete'}
+ className='signup-team-login'
+ >
<FormattedMessage
- id='login.noAccount'
- defaultMessage="Don't have an account? "
+ id='login.create'
+ defaultMessage='Create one now'
/>
- <Link
- to={'/signup_user_complete/?id=' + currentTeam.invite_id}
- className='signup-team-login'
- >
- <FormattedMessage
- id='login.create'
- defaultMessage='Create one now'
- />
- </Link>
- </span>
- </div>
- );
- }
+ </Link>
+ </span>
+ </div>
+ );
let forgotPassword;
if (usernameSigninEnabled || emailSigninEnabled) {
forgotPassword = (
<div className='form-group'>
- <Link to={'/' + teamName + '/reset_password'}>
+ <Link to={'/reset_password'}>
<FormattedMessage
id='login.forgot'
defaultMessage='I forgot my password'
@@ -346,23 +352,6 @@ export default class Login extends React.Component {
);
}
- let teamSignUp;
- if (global.window.mm_config.EnableTeamCreation === 'true' && !Utils.isMobileApp()) {
- teamSignUp = (
- <div className='margin--extra'>
- <Link
- to='/'
- className='signup-team-login'
- >
- <FormattedMessage
- id='login.createTeam'
- defaultMessage='Create a new team'
- />
- </Link>
- </div>
- );
- }
-
return (
<div>
{extraBox}
@@ -372,16 +361,10 @@ export default class Login extends React.Component {
{ldapLogin}
{userSignUp}
{forgotPassword}
- {teamSignUp}
</div>
);
}
render() {
- const currentTeam = this.state.currentTeam;
- if (currentTeam == null || !this.state.doneCheckLogin) {
- return <div/>;
- }
-
let content;
let customContent;
let customClass;
@@ -395,7 +378,7 @@ export default class Login extends React.Component {
/>
);
} else {
- content = this.createLoginOptions(currentTeam);
+ content = this.createLoginOptions();
customContent = this.createCustomLogin();
if (customContent) {
customClass = 'branded';
@@ -404,36 +387,23 @@ export default class Login extends React.Component {
return (
<div>
- <div className='signup-header'>
- <Link to='/'>
- <span className='fa fa-chevron-left'/>
- <FormattedMessage
- id='web.header.back'
- />
- </Link>
- </div>
+ <ErrorBar/>
<div className='col-sm-12'>
<div className={'signup-team__container ' + customClass}>
<div className='signup__markdown'>
{customContent}
</div>
+ <img
+ className='signup-team-logo'
+ src={logoImage}
+ />
+ <h1>{global.window.mm_config.SiteName}</h1>
+ <h4 className='color--light'>
+ <FormattedMessage
+ id='web.root.singup_info'
+ />
+ </h4>
<div className='signup__content'>
- <h5 className='margin--less'>
- <FormattedMessage
- id='login.signTo'
- defaultMessage='Sign in to:'
- />
- </h5>
- <h2 className='signup-team__name'>{currentTeam.display_name}</h2>
- <h2 className='signup-team__subdomain'>
- <FormattedMessage
- id='login.on'
- defaultMessage='on {siteName}'
- values={{
- siteName: global.window.mm_config.SiteName
- }}
- />
- </h2>
{content}
</div>
</div>
diff --git a/webapp/components/member_list_team.jsx b/webapp/components/member_list_team.jsx
index bb5eee496..d0714e942 100644
--- a/webapp/components/member_list_team.jsx
+++ b/webapp/components/member_list_team.jsx
@@ -4,6 +4,8 @@
import FilteredUserList from './filtered_user_list.jsx';
import TeamMembersDropdown from './team_members_dropdown.jsx';
import UserStore from 'stores/user_store.jsx';
+import TeamStore from 'stores/team_store.jsx';
+import * as AsyncClient from 'utils/async_client.jsx';
import React from 'react';
@@ -13,18 +15,23 @@ export default class MemberListTeam extends React.Component {
this.getUsers = this.getUsers.bind(this);
this.onChange = this.onChange.bind(this);
+ this.onTeamChange = this.onTeamChange.bind(this);
this.state = {
- users: this.getUsers()
+ users: this.getUsers(),
+ teamMembers: TeamStore.getMembersForTeam()
};
}
componentDidMount() {
UserStore.addChangeListener(this.onChange);
+ TeamStore.addChangeListener(this.onTeamChange);
+ AsyncClient.getTeamMembers(TeamStore.getCurrentId());
}
componentWillUnmount() {
UserStore.removeChangeListener(this.onChange);
+ TeamStore.removeChangeListener(this.onTeamChange);
}
getUsers() {
@@ -46,11 +53,18 @@ export default class MemberListTeam extends React.Component {
});
}
+ onTeamChange() {
+ this.setState({
+ teamMembers: TeamStore.getMembersForTeam()
+ });
+ }
+
render() {
return (
<FilteredUserList
style={this.props.style}
users={this.state.users}
+ teamMembers={this.state.teamMembers}
actions={[TeamMembersDropdown]}
/>
);
diff --git a/webapp/components/more_channels.jsx b/webapp/components/more_channels.jsx
index 5ccf9c11e..3ab05341b 100644
--- a/webapp/components/more_channels.jsx
+++ b/webapp/components/more_channels.jsx
@@ -4,7 +4,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
-import * as client from 'utils/client.jsx';
+import client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import LoadingScreen from './loading_screen.jsx';
diff --git a/webapp/components/navbar.jsx b/webapp/components/navbar.jsx
index e4e64c12e..919a72d0a 100644
--- a/webapp/components/navbar.jsx
+++ b/webapp/components/navbar.jsx
@@ -18,7 +18,7 @@ import UserStore from 'stores/user_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import TeamStore from 'stores/team_store.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx';
diff --git a/webapp/components/navbar_dropdown.jsx b/webapp/components/navbar_dropdown.jsx
index da1ae237e..19b99a14d 100644
--- a/webapp/components/navbar_dropdown.jsx
+++ b/webapp/components/navbar_dropdown.jsx
@@ -6,6 +6,7 @@ import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
+import TeamStore from 'stores/team_store.jsx';
import AboutBuildModal from './about_build_modal.jsx';
import TeamMembersModal from './team_members_modal.jsx';
import ToggleModalButton from './toggle_modal_button.jsx';
@@ -25,10 +26,13 @@ export default class NavbarDropdown extends React.Component {
this.handleAboutModal = this.handleAboutModal.bind(this);
this.aboutModalDismissed = this.aboutModalDismissed.bind(this);
+ this.onTeamChange = this.onTeamChange.bind(this);
this.state = {
showUserSettingsModal: false,
- showAboutModal: false
+ showAboutModal: false,
+ teams: TeamStore.getAll(),
+ teamMembers: TeamStore.getTeamMembers()
};
}
handleAboutModal() {
@@ -37,6 +41,7 @@ export default class NavbarDropdown extends React.Component {
aboutModalDismissed() {
this.setState({showAboutModal: false});
}
+
componentDidMount() {
$(ReactDOM.findDOMNode(this.refs.dropdown)).on('hide.bs.dropdown', () => {
$('.sidebar--left .dropdown-menu').scrollTop(0);
@@ -45,10 +50,22 @@ export default class NavbarDropdown extends React.Component {
this.blockToggle = false;
}, 100);
});
+
+ TeamStore.addChangeListener(this.onTeamChange);
+ }
+
+ onTeamChange() {
+ this.setState({
+ teams: TeamStore.getAll(),
+ teamMembers: TeamStore.getTeamMembers()
+ });
}
+
componentWillUnmount() {
$(ReactDOM.findDOMNode(this.refs.dropdown)).off('hide.bs.dropdown');
+ TeamStore.removeChangeListener(this.onTeamChange);
}
+
render() {
var teamLink = '';
var inviteLink = '';
@@ -130,7 +147,7 @@ export default class NavbarDropdown extends React.Component {
if (isAdmin || window.EnableAdminOnlyIntegrations !== 'true') {
integrationsLink = (
<li>
- <Link to={'/settings/integrations'}>
+ <Link to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations'}>
<FormattedMessage
id='navbar_dropdown.integrations'
defaultMessage='Integrations'
@@ -163,8 +180,7 @@ export default class NavbarDropdown extends React.Component {
<li key='newTeam_li'>
<Link
key='newTeam_a'
- target='_blank'
- to={Utils.getWindowLocationOrigin() + '/signup_team'}
+ to='/create_team'
>
<FormattedMessage
id='navbar_dropdown.create'
@@ -175,6 +191,34 @@ export default class NavbarDropdown extends React.Component {
);
}
+ if (this.state.teamMembers && this.state.teamMembers.length > 0) {
+ teams.push(
+ <li
+ key='teamDiv'
+ className='divider'
+ ></li>
+ );
+
+ for (var index in this.state.teamMembers) {
+ if (this.state.teamMembers.hasOwnProperty(index)) {
+ var teamMember = this.state.teamMembers[index];
+ var team = this.state.teams[teamMember.team_id];
+
+ if (team.name !== this.props.teamName) {
+ teams.push(
+ <li key={'team_' + team.name}>
+ <Link
+ to={'/' + team.name + '/channels/town-square'}
+ >
+ {team.display_name}
+ </Link>
+ </li>
+ );
+ }
+ }
+ }
+ }
+
let helpLink = null;
if (global.window.mm_config.HelpLink) {
helpLink = (
@@ -245,12 +289,15 @@ export default class NavbarDropdown extends React.Component {
{inviteLink}
{teamLink}
<li>
- <Link to={'/' + this.props.teamName + '/logout'}>
+ <a
+ href='#'
+ onClick={GlobalActions.emitUserLoggedOutEvent}
+ >
<FormattedMessage
id='navbar_dropdown.logout'
defaultMessage='Logout'
/>
- </Link>
+ </a>
</li>
{adminDivider}
{teamSettings}
diff --git a/webapp/components/needs_team.jsx b/webapp/components/needs_team.jsx
index f624b1ebc..59797f086 100644
--- a/webapp/components/needs_team.jsx
+++ b/webapp/components/needs_team.jsx
@@ -1,22 +1,156 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+import React from 'react';
+
+import $ from 'jquery';
+
+import {browserHistory} from 'react-router';
+import * as Utils from 'utils/utils.jsx';
+import * as AsyncClient from 'utils/async_client.jsx';
+import TeamStore from 'stores/team_store.jsx';
+import UserStore from 'stores/user_store.jsx';
+import PreferenceStore from 'stores/preference_store.jsx';
+import ChannelStore from 'stores/channel_store.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
+import Constants from 'utils/constants.jsx';
+const TutorialSteps = Constants.TutorialSteps;
+const Preferences = Constants.Preferences;
-import React from 'react';
+import ErrorBar from 'components/error_bar.jsx';
+import SidebarRight from 'components/sidebar_right.jsx';
+import SidebarRightMenu from 'components/sidebar_right_menu.jsx';
+import Navbar from 'components/navbar.jsx';
+
+// Modals
+import GetPostLinkModal from 'components/get_post_link_modal.jsx';
+import GetTeamInviteLinkModal from 'components/get_team_invite_link_modal.jsx';
+import EditPostModal from 'components/edit_post_modal.jsx';
+import DeletePostModal from 'components/delete_post_modal.jsx';
+import MoreChannelsModal from 'components/more_channels.jsx';
+import TeamSettingsModal from 'components/team_settings_modal.jsx';
+import RemovedFromChannelModal from 'components/removed_from_channel_modal.jsx';
+import RegisterAppModal from 'components/register_app_modal.jsx';
+import ImportThemeModal from 'components/user_settings/import_theme_modal.jsx';
+import InviteMemberModal from 'components/invite_member_modal.jsx';
+import SelectTeamModal from 'components/admin_console/select_team_modal.jsx';
export default class NeedsTeam extends React.Component {
+ constructor(params) {
+ super(params);
+
+ this.onChanged = this.onChanged.bind(this);
+
+ this.state = {
+ profiles: UserStore.getProfiles(),
+ team: TeamStore.getCurrent()
+ };
+ }
+
+ onChanged() {
+ this.setState({
+ profiles: UserStore.getProfiles(),
+ team: TeamStore.getCurrent()
+ });
+ }
+
componentWillMount() {
- GlobalActions.loadTeamRequiredPage();
+ UserStore.addChangeListener(this.onChanged);
+ TeamStore.addChangeListener(this.onChanged);
+
+ // Emit view action
+ GlobalActions.viewLoggedIn();
+
+ // Go to tutorial if we are first arrivign
+ const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
+ if (tutorialStep <= TutorialSteps.INTRO_SCREENS) {
+ browserHistory.push(Utils.getTeamURLFromAddressBar() + '/tutorial');
+ }
+
+ // Set up tracking for whether the window is active
+ window.isActive = true;
+ $(window).on('focus', () => {
+ AsyncClient.updateLastViewedAt();
+ ChannelStore.resetCounts(ChannelStore.getCurrentId());
+ ChannelStore.emitChange();
+ window.isActive = true;
+ });
+ $(window).on('blur', () => {
+ window.isActive = false;
+ });
+ }
+
+ componentWillUnmount() {
+ UserStore.removeChangeListener(this.onChanged);
+ TeamStore.addChangeListener(this.onChanged);
+ $(window).off('focus');
+ $(window).off('blur');
}
+
render() {
- return this.props.children;
+ let content = [];
+ if (this.props.children) {
+ content = this.props.children;
+ } else {
+ content.push(
+ this.props.navbar
+ );
+ content.push(
+ this.props.sidebar
+ );
+ content.push(
+ <div
+ key='inner-wrap'
+ className='inner-wrap channel__wrap'
+ >
+ <div className='row header'>
+ <div id='navbar'>
+ <Navbar/>
+ </div>
+ </div>
+ <div className='row main'>
+ {React.cloneElement(this.props.center, {
+ user: this.props.user,
+ profiles: this.state.profiles,
+ team: this.state.team
+ })}
+ </div>
+ </div>
+ );
+ }
+ return (
+ <div className='channel-view'>
+ <ErrorBar/>
+ <div className='container-fluid'>
+ <SidebarRight/>
+ <SidebarRightMenu/>
+ {content}
+
+ <GetPostLinkModal/>
+ <GetTeamInviteLinkModal/>
+ <InviteMemberModal/>
+ <ImportThemeModal/>
+ <TeamSettingsModal/>
+ <MoreChannelsModal/>
+ <EditPostModal/>
+ <DeletePostModal/>
+ <RemovedFromChannelModal/>
+ <RegisterAppModal/>
+ <SelectTeamModal/>
+ </div>
+ </div>
+ );
}
}
-NeedsTeam.defaultProps = {
-};
-
NeedsTeam.propTypes = {
- children: React.PropTypes.object
+ children: React.PropTypes.oneOfType([
+ React.PropTypes.arrayOf(React.PropTypes.element),
+ React.PropTypes.element
+ ]),
+ navbar: React.PropTypes.element,
+ sidebar: React.PropTypes.element,
+ center: React.PropTypes.element,
+ params: React.PropTypes.object,
+ user: React.PropTypes.object
};
diff --git a/webapp/components/new_channel_flow.jsx b/webapp/components/new_channel_flow.jsx
index 82494dac0..7019da4aa 100644
--- a/webapp/components/new_channel_flow.jsx
+++ b/webapp/components/new_channel_flow.jsx
@@ -2,7 +2,7 @@
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import UserStore from 'stores/user_store.jsx';
import NewChannelModal from './new_channel_modal.jsx';
@@ -106,11 +106,7 @@ class NewChannelFlow extends React.Component {
(data) => {
Client.getChannel(
data.id,
- (data2, textStatus, xhr) => {
- if (xhr.status === 304 || !data2) {
- return;
- }
-
+ (data2) => {
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_CHANNEL,
channel: data2.channel,
diff --git a/webapp/components/notify_counts.jsx b/webapp/components/notify_counts.jsx
index acc64dfb0..9238c8736 100644
--- a/webapp/components/notify_counts.jsx
+++ b/webapp/components/notify_counts.jsx
@@ -32,17 +32,22 @@ export default class NotifyCounts extends React.Component {
this.onListenerChange = this.onListenerChange.bind(this);
this.state = getCountsStateFromStores();
+ this.mounted = false;
}
componentDidMount() {
+ this.mounted = true;
ChannelStore.addChangeListener(this.onListenerChange);
}
componentWillUnmount() {
+ this.mounted = false;
ChannelStore.removeChangeListener(this.onListenerChange);
}
onListenerChange() {
- var newState = getCountsStateFromStores();
- if (!utils.areObjectsEqual(newState, this.state)) {
- this.setState(newState);
+ if (this.mounted) {
+ var newState = getCountsStateFromStores();
+ if (!utils.areObjectsEqual(newState, this.state)) {
+ this.setState(newState);
+ }
}
}
render() {
diff --git a/webapp/components/password_reset_form.jsx b/webapp/components/password_reset_form.jsx
index 863420902..23b8952cc 100644
--- a/webapp/components/password_reset_form.jsx
+++ b/webapp/components/password_reset_form.jsx
@@ -2,7 +2,7 @@
// See License.txt for license information.
import ReactDOM from 'react-dom';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
@@ -40,16 +40,12 @@ class PasswordResetForm extends React.Component {
error: null
});
- const data = {};
- data.new_password = password;
- data.hash = this.props.location.query.h;
- data.data = this.props.location.query.d;
- data.name = this.props.params.team;
-
- Client.resetPassword(data,
+ Client.resetPassword(
+ this.props.location.query.code,
+ password,
() => {
this.setState({error: null});
- browserHistory.push('/' + this.props.params.team + '/login');
+ browserHistory.push('/login?extra=' + Constants.PASSWORD_CHANGE);
},
(err) => {
this.setState({error: err.message});
diff --git a/webapp/components/password_reset_send_link.jsx b/webapp/components/password_reset_send_link.jsx
index e3ab8949e..65d9439bd 100644
--- a/webapp/components/password_reset_send_link.jsx
+++ b/webapp/components/password_reset_send_link.jsx
@@ -4,7 +4,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
-import * as client from 'utils/client.jsx';
+import client from 'utils/web_client.jsx';
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
@@ -43,10 +43,8 @@ class PasswordResetSendLink extends React.Component {
error: ''
});
- var data = {};
- data.email = email;
- data.name = this.props.params.team;
- client.sendPasswordReset(data,
+ client.sendPasswordReset(
+ email,
() => {
this.setState({
error: null,
diff --git a/webapp/components/popover_list_members.jsx b/webapp/components/popover_list_members.jsx
index 8c3c381af..387c37ab5 100644
--- a/webapp/components/popover_list_members.jsx
+++ b/webapp/components/popover_list_members.jsx
@@ -7,6 +7,7 @@ import UserStore from 'stores/user_store.jsx';
import {Popover, Overlay} from 'react-bootstrap';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
+import Client from 'utils/web_client.jsx';
import {FormattedMessage} from 'react-intl';
import {browserHistory} from 'react-router';
@@ -97,7 +98,7 @@ export default class PopoverListMembers extends React.Component {
className='more-modal__image'
width='26px'
height='26px'
- src={`/api/v1/users/${m.id}/image?time=${m.update_at}`}
+ src={`${Client.getUsersRoute()}/${m.id}/image?time=${m.update_at}`}
/>
<div className='more-modal__details'>
<div
diff --git a/webapp/components/post.jsx b/webapp/components/post.jsx
index 7fabec741..ae3fa9c98 100644
--- a/webapp/components/post.jsx
+++ b/webapp/components/post.jsx
@@ -10,7 +10,7 @@ import ChannelStore from 'stores/channel_store.jsx';
import Constants from 'utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
@@ -48,7 +48,7 @@ export default class Post extends React.Component {
e.preventDefault();
var post = this.props.post;
- Client.createPost(post, post.channel_id,
+ Client.createPost(post,
(data) => {
AsyncClient.getPosts();
diff --git a/webapp/components/register_app_modal.jsx b/webapp/components/register_app_modal.jsx
index c632233cf..82a095c75 100644
--- a/webapp/components/register_app_modal.jsx
+++ b/webapp/components/register_app_modal.jsx
@@ -1,7 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import ModalStore from 'stores/modal_store.jsx';
import {Modal} from 'react-bootstrap';
diff --git a/webapp/components/rename_channel_modal.jsx b/webapp/components/rename_channel_modal.jsx
index 3e47847e7..ed045da91 100644
--- a/webapp/components/rename_channel_modal.jsx
+++ b/webapp/components/rename_channel_modal.jsx
@@ -3,7 +3,7 @@
import ReactDOM from 'react-dom';
import * as Utils from 'utils/utils.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import Constants from 'utils/constants.jsx';
diff --git a/webapp/components/rhs_comment.jsx b/webapp/components/rhs_comment.jsx
index 53170ee15..709865dc1 100644
--- a/webapp/components/rhs_comment.jsx
+++ b/webapp/components/rhs_comment.jsx
@@ -9,7 +9,7 @@ import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
import FileAttachmentList from './file_attachment_list.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
var ActionTypes = Constants.ActionTypes;
import * as TextFormatting from 'utils/text_formatting.jsx';
@@ -43,7 +43,7 @@ class RhsComment extends React.Component {
e.preventDefault();
var post = this.props.post;
- Client.createPost(post, post.channel_id,
+ Client.createPost(post,
(data) => {
AsyncClient.getPosts(post.channel_id);
@@ -261,7 +261,7 @@ class RhsComment extends React.Component {
<div className='post__content'>
<div className='post__img'>
<img
- src={'/api/v1/users/' + post.user_id + '/image?time=' + timestamp}
+ src={Client.getUsersRoute() + '/' + post.user_id + '/image?time=' + timestamp}
height='36'
width='36'
/>
diff --git a/webapp/components/rhs_header_post.jsx b/webapp/components/rhs_header_post.jsx
index 189ee0acb..493040800 100644
--- a/webapp/components/rhs_header_post.jsx
+++ b/webapp/components/rhs_header_post.jsx
@@ -3,6 +3,7 @@
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import Constants from 'utils/constants.jsx';
+import * as GlobalActions from 'action_creators/global_actions.jsx';
import {FormattedMessage} from 'react-intl';
@@ -21,16 +22,7 @@ export default class RhsHeaderPost extends React.Component {
}
handleClose(e) {
e.preventDefault();
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_SEARCH,
- results: null
- });
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_POST_SELECTED,
- postId: null
- });
+ GlobalActions.emitCloseRightHandSide();
}
handleBack(e) {
e.preventDefault();
diff --git a/webapp/components/root.jsx b/webapp/components/root.jsx
index 59364d085..0adbc7f04 100644
--- a/webapp/components/root.jsx
+++ b/webapp/components/root.jsx
@@ -1,10 +1,10 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import $ from 'jquery';
+//import $ from 'jquery';
+//import Client from 'utils/web_client.jsx';
+
import * as GlobalActions from 'action_creators/global_actions.jsx';
-import * as Client from 'utils/client.jsx';
-import BrowserStore from 'stores/browser_store.jsx';
import LocalizationStore from 'stores/localization_store.jsx';
import {IntlProvider} from 'react-intl';
@@ -14,6 +14,7 @@ import React from 'react';
import FastClick from 'fastclick';
import {browserHistory} from 'react-router';
+import UserStore from 'stores/user_store.jsx';
export default class Root extends React.Component {
constructor(props) {
@@ -29,15 +30,16 @@ export default class Root extends React.Component {
localizationChanged() {
this.setState({locale: LocalizationStore.getLocale(), translations: LocalizationStore.getTranslations()});
}
+
redirectIfNecessary(props) {
if (props.location.pathname === '/') {
- Client.getMeLoggedIn((data) => {
- if (!data || data.logged_in === 'false') {
- browserHistory.push('/signup_team');
- } else {
- browserHistory.push('/' + data.team_name + '/channels/town-square');
- }
- });
+ if (UserStore.getNoAccounts()) {
+ browserHistory.push('/signup_user_complete');
+ } else if (UserStore.getCurrentUser()) {
+ browserHistory.push('/select_team');
+ } else {
+ browserHistory.push('/login');
+ }
}
}
componentWillReceiveProps(newProps) {
@@ -47,28 +49,6 @@ export default class Root extends React.Component {
// Setup localization listener
LocalizationStore.addChangeListener(this.localizationChanged);
- // Browser store check version
- BrowserStore.checkVersion();
-
- window.onerror = (msg, url, line, column, stack) => {
- var l = {};
- l.level = 'ERROR';
- l.message = 'msg: ' + msg + ' row: ' + line + ' col: ' + column + ' stack: ' + stack + ' url: ' + url;
-
- $.ajax({
- url: '/api/v1/admin/log_client',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(l)
- });
-
- if (window.mm_config.EnableDeveloper === 'true') {
- window.ErrorStore.storeLastError({message: 'DEVELOPER MODE: A javascript error has occured. Please use the javascript console to capture and report the error (row: ' + line + ' col: ' + column + ').'});
- window.ErrorStore.emitChange();
- }
- };
-
// Ya....
/*eslint-disable */
if (window.mm_config.SegmentDeveloperKey != null && window.mm_config.SegmentDeveloperKey !== "") {
@@ -76,11 +56,7 @@ export default class Root extends React.Component {
analytics.load(window.mm_config.SegmentDeveloperKey);
analytics.page();
}}();
- } else {
- global.window.analytics = {};
- global.window.analytics.page = function(){};
- global.window.analytics.track = function(){};
- }
+ }
/*eslint-enable */
// Fastclick
diff --git a/webapp/components/search_bar.jsx b/webapp/components/search_bar.jsx
index caaf0f844..1156ac0f1 100644
--- a/webapp/components/search_bar.jsx
+++ b/webapp/components/search_bar.jsx
@@ -3,7 +3,7 @@
import $ from 'jquery';
import ReactDOM from 'react-dom';
-import * as client from 'utils/client.jsx';
+import client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import SearchStore from 'stores/search_store.jsx';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
diff --git a/webapp/components/select_team/select_team.jsx b/webapp/components/select_team/select_team.jsx
new file mode 100644
index 000000000..5804a641e
--- /dev/null
+++ b/webapp/components/select_team/select_team.jsx
@@ -0,0 +1,257 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import UserStore from 'stores/user_store.jsx';
+import TeamStore from 'stores/team_store.jsx';
+import * as Utils from 'utils/utils.jsx';
+import ErrorBar from 'components/error_bar.jsx';
+import LoadingScreen from 'components/loading_screen.jsx';
+import Client from 'utils/web_client.jsx';
+import * as AsyncClient from 'utils/async_client.jsx';
+import * as GlobalActions from 'action_creators/global_actions.jsx';
+
+import * as TextFormatting from 'utils/text_formatting.jsx';
+
+import {Link} from 'react-router';
+
+import {FormattedMessage} from 'react-intl';
+
+//import {browserHistory, Link} from 'react-router';
+
+import React from 'react';
+import logoImage from 'images/logo.png';
+
+export default class Login extends React.Component {
+
+ constructor(props) {
+ super(props);
+ this.onTeamChange = this.onTeamChange.bind(this);
+
+ const state = this.getStateFromStores(false);
+ this.state = state;
+ }
+
+ componentDidMount() {
+ TeamStore.addChangeListener(this.onTeamChange);
+ AsyncClient.getAllTeamListings();
+ }
+
+ componentWillUnmount() {
+ TeamStore.removeChangeListener(this.onTeamChange);
+ }
+
+ onTeamChange() {
+ this.setState(this.getStateFromStores(true));
+ }
+
+ getStateFromStores(loaded) {
+ return {
+ teams: TeamStore.getAll(),
+ teamMembers: TeamStore.getTeamMembers(),
+ teamListings: TeamStore.getTeamListings(),
+ loaded
+ };
+ }
+
+ createCustomLogin() {
+ if (global.window.mm_license.IsLicensed === 'true' &&
+ global.window.mm_license.CustomBrand === 'true' &&
+ global.window.mm_config.EnableCustomBrand === 'true') {
+ const text = global.window.mm_config.CustomBrandText || '';
+
+ return (
+ <div>
+ <img
+ src={Client.getAdminRoute() + '/get_brand_image'}
+ />
+ <p dangerouslySetInnerHTML={{__html: TextFormatting.formatText(text)}}/>
+ </div>
+ );
+ }
+
+ return null;
+ }
+
+ render() {
+ var content;
+
+ let customClass;
+ const customContent = this.createCustomLogin();
+ if (customContent) {
+ customClass = 'branded';
+ }
+
+ var teamContents = [];
+ var isAlreadyMember = new Map();
+
+ for (var index in this.state.teamMembers) {
+ if (this.state.teamMembers.hasOwnProperty(index)) {
+ var teamMember = this.state.teamMembers[index];
+ var team = this.state.teams[teamMember.team_id];
+ isAlreadyMember[teamMember.team_id] = true;
+ teamContents.push(
+ <div
+ key={'team_' + team.name}
+ className='signup-team-dir'
+ >
+ <Link
+ to={'/' + team.name + '/channels/town-square'}
+ >
+ <span className='signup-team-dir__name'>{team.display_name}</span>
+ <span
+ className='glyphicon glyphicon-menu-right right signup-team-dir__arrow'
+ aria-hidden='true'
+ />
+ </Link>
+ </div>
+ );
+ }
+ }
+
+ if (!teamContents || teamContents.length === 0) {
+ teamContents = (
+ <div className='signup-team-dir-err'>
+ <div>
+ <FormattedMessage
+ id='signup_team.no_teams'
+ defaultMessage='You do not appear to be a member of any team. Please ask your administrator for an invite, join an open team if one exists or possibly create a new team.'
+ />
+ </div>
+ </div>
+ );
+ }
+
+ content = (
+ <div className='signup__content'>
+ <h4>
+ <FormattedMessage
+ id='signup_team.choose'
+ defaultMessage='Teams you are a member of:'
+ />
+ </h4>
+ <div className='signup-team-all'>
+ {teamContents}
+ </div>
+ </div>
+ );
+
+ var openTeamContents = [];
+
+ for (var id in this.state.teamListings) {
+ if (this.state.teamListings.hasOwnProperty(id) && !isAlreadyMember[id]) {
+ var openTeam = this.state.teamListings[id];
+ openTeamContents.push(
+ <div
+ key={'team_' + openTeam.name}
+ className='signup-team-dir'
+ >
+ <Link
+ to={`/signup_user_complete/?id=${openTeam.invite_id}`}
+ >
+ <span className='signup-team-dir__name'>{openTeam.display_name}</span>
+ <span
+ className='glyphicon glyphicon-menu-right right signup-team-dir__arrow'
+ aria-hidden='true'
+ />
+ </Link>
+ </div>
+ );
+ }
+ }
+
+ var openContent;
+ if (openTeamContents.length > 0) {
+ openContent = (
+ <div className='signup__content'>
+ <h4>
+ <FormattedMessage
+ id='signup_team.join_open'
+ defaultMessage='Open teams you can join: '
+ />
+ </h4>
+ <div className='signup-team-all'>
+ {openTeamContents}
+ </div>
+ </div>
+ );
+ }
+
+ if (!this.state.loaded) {
+ openContent = <LoadingScreen/>;
+ }
+
+ var isSystemAdmin = Utils.isSystemAdmin(UserStore.getCurrentUser().roles);
+
+ let teamSignUp;
+ if (isSystemAdmin || (global.window.mm_config.EnableTeamCreation === 'true' && !Utils.isMobileApp())) {
+ teamSignUp = (
+ <div className='margin--extra'>
+ <Link
+ to='/create_team'
+ className='signup-team-login'
+ >
+ <FormattedMessage
+ id='login.createTeam'
+ defaultMessage='Create a new team'
+ />
+ </Link>
+ </div>
+ );
+ }
+
+ let adminConsoleLink;
+ if (isSystemAdmin) {
+ adminConsoleLink = (
+ <div className='margin--extra'>
+ <Link
+ to='/admin_console'
+ className='signup-team-login'
+ >
+ <FormattedMessage
+ id='navbar_dropdown.console'
+ defaultMessage='System Console'
+ />
+ </Link>
+ </div>
+ );
+ }
+
+ return (
+ <div>
+ <ErrorBar/>
+ <div className='signup-header'>
+ <a
+ href='#'
+ onClick={GlobalActions.emitUserLoggedOutEvent}
+ >
+ <span className='fa fa-chevron-left'/>
+ <FormattedMessage
+ id='navbar_dropdown.logout'
+ />
+ </a>
+ </div>
+ <div className='col-sm-12'>
+ <div className={'signup-team__container ' + customClass}>
+ <div className='signup__markdown'>
+ {customContent}
+ </div>
+ <img
+ className='signup-team-logo'
+ src={logoImage}
+ />
+ <h1>{global.window.mm_config.SiteName}</h1>
+ <h4 className='color--light'>
+ <FormattedMessage
+ id='web.root.singup_info'
+ />
+ </h4>
+ {content}
+ {openContent}
+ {teamSignUp}
+ {adminConsoleLink}
+ </div>
+ </div>
+ </div>
+ );
+ }
+}
diff --git a/webapp/components/should_verify_email.jsx b/webapp/components/should_verify_email.jsx
index 5103452b0..a95101ba1 100644
--- a/webapp/components/should_verify_email.jsx
+++ b/webapp/components/should_verify_email.jsx
@@ -2,7 +2,7 @@
// See License.txt for license information.
import {FormattedMessage} from 'react-intl';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import React from 'react';
import {Link} from 'react-router';
@@ -18,19 +18,19 @@ export default class ShouldVerifyEmail extends React.Component {
};
}
handleResend() {
- const teamName = this.props.location.query.teamname;
const email = this.props.location.query.email;
this.setState({resendStatus: 'sending'});
- Client.resendVerification(() => {
- this.setState({resendStatus: 'success'});
- },
- () => {
- this.setState({resendStatus: 'failure'});
- },
- teamName,
- email);
+ Client.resendVerification(
+ email,
+ () => {
+ this.setState({resendStatus: 'success'});
+ },
+ () => {
+ this.setState({resendStatus: 'failure'});
+ }
+ );
}
render() {
let resendConfirm = '';
diff --git a/webapp/components/sidebar_header.jsx b/webapp/components/sidebar_header.jsx
index ec3a03d17..143a3458a 100644
--- a/webapp/components/sidebar_header.jsx
+++ b/webapp/components/sidebar_header.jsx
@@ -7,6 +7,7 @@ import NavbarDropdown from './navbar_dropdown.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
import * as Utils from 'utils/utils.jsx';
+import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
const Preferences = Constants.Preferences;
@@ -61,7 +62,7 @@ export default class SidebarHeader extends React.Component {
profilePicture = (
<img
className='user__picture'
- src={'/api/v1/users/' + me.id + '/image?time=' + me.update_at}
+ src={Client.getUsersRoute() + '/' + me.id + '/image?time=' + me.update_at}
/>
);
}
diff --git a/webapp/components/sidebar_right_menu.jsx b/webapp/components/sidebar_right_menu.jsx
index c7e6577fc..42bc7ce53 100644
--- a/webapp/components/sidebar_right_menu.jsx
+++ b/webapp/components/sidebar_right_menu.jsx
@@ -228,13 +228,16 @@ export default class SidebarRightMenu extends React.Component {
{manageLink}
{consoleLink}
<li>
- <Link to={Utils.getTeamURLFromAddressBar() + '/logout'}>
+ <a
+ href='#'
+ onClick={GlobalActions.emitUserLoggedOutEvent}
+ >
<i className='fa fa-sign-out'></i>
<FormattedMessage
id='sidebar_right_menu.logout'
defaultMessage='Logout'
/>
- </Link>
+ </a>
</li>
<li className='divider'></li>
{helpLink}
diff --git a/webapp/components/signup_team.jsx b/webapp/components/signup_team.jsx
deleted file mode 100644
index 634aa6e68..000000000
--- a/webapp/components/signup_team.jsx
+++ /dev/null
@@ -1,235 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ChoosePage from './team_signup_choose_auth.jsx';
-import EmailSignUpPage from './team_signup_with_email.jsx';
-import SSOSignupPage from './team_signup_with_sso.jsx';
-import LdapSignUpPage from './team_signup_with_ldap.jsx';
-import Constants from 'utils/constants.jsx';
-import TeamStore from 'stores/team_store.jsx';
-import * as AsyncClient from 'utils/async_client.jsx';
-
-import {FormattedMessage} from 'react-intl';
-
-import React from 'react';
-import {Link} from 'react-router';
-
-import logoImage from 'images/logo.png';
-
-export default class TeamSignUp extends React.Component {
- constructor(props) {
- super(props);
-
- this.updatePage = this.updatePage.bind(this);
- this.onTeamUpdate = this.onTeamUpdate.bind(this);
-
- var count = 0;
-
- if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
- count = count + 1;
- }
-
- if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
- count = count + 1;
- }
-
- if (global.window.mm_config.EnableLdap === 'true') {
- count = count + 1;
- }
-
- if (count > 1) {
- this.state = {page: 'choose'};
- } else if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
- this.state = {page: 'email'};
- } else if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
- this.state = {page: 'gitlab'};
- } else if (global.window.mm_config.EnableLdap === 'true') {
- this.state = {page: 'ldap'};
- } else {
- this.state = {page: 'none'};
- }
- }
-
- updatePage(page) {
- this.setState({page});
- }
-
- componentWillMount() {
- if (global.window.mm_config.EnableTeamListing === 'true') {
- AsyncClient.getAllTeams();
- this.onTeamUpdate();
- }
- }
-
- componentDidMount() {
- TeamStore.addChangeListener(this.onTeamUpdate);
- }
-
- componentWillUnmount() {
- TeamStore.removeChangeListener(this.onTeamUpdate);
- }
-
- onTeamUpdate() {
- this.setState({
- teams: TeamStore.getAll()
- });
- }
-
- render() {
- let teamListing = null;
-
- if (global.window.mm_config.EnableTeamListing === 'true') {
- if (this.state.teams == null) {
- teamListing = (<div/>);
- } else if (this.state.teams.length === 0) {
- if (global.window.mm_config.EnableTeamCreation !== 'true') {
- teamListing = (
- <div>
- <FormattedMessage
- id='signup_team.noTeams'
- defaultMessage='There are no teams included in the Team Directory and team creation has been disabled.'
- />
- </div>
- );
- }
- } else {
- teamListing = (
- <div>
- <h4>
- <FormattedMessage
- id='signup_team.choose'
- defaultMessage='Choose a Team'
- />
- </h4>
- <div className='signup-team-all'>
- {
- Object.values(this.state.teams).map((team) => {
- if (team.allow_team_listing) {
- return (
- <div
- key={'team_' + team.name}
- className='signup-team-dir'
- >
- <Link
- to={'/' + team.name}
- >
- <span className='signup-team-dir__name'>{team.display_name}</span>
- <span
- className='glyphicon glyphicon-menu-right right signup-team-dir__arrow'
- aria-hidden='true'
- />
- </Link>
- </div>
- );
- }
- return null;
- })
- }
- </div>
- <h4>
- <FormattedMessage
- id='signup_team.createTeam'
- defaultMessage='Or Create a Team'
- />
- </h4>
- </div>
- );
- }
- }
-
- let signupMethod = null;
- let goBack = (
- <div className='signup-header'>
- <a
- href='#'
- onClick={
- (e) => {
- e.preventDefault();
- this.updatePage('choose');
- }
- }
- >
- <span className='fa fa-chevron-left'/>
- <FormattedMessage
- id='web.header.back'
- />
- </a>
- </div>
- );
-
- if (global.window.mm_config.EnableTeamCreation !== 'true') {
- if (teamListing == null) {
- signupMethod = (
- <FormattedMessage
- id='signup_team.disabled'
- defaultMessage='Team creation has been disabled. Please contact an administrator for access.'
- />
- );
- }
- } else if (this.state.page === 'choose') {
- signupMethod = (
- <ChoosePage
- updatePage={this.updatePage}
- />
- );
- goBack = null;
- } else if (this.state.page === 'email') {
- signupMethod = (
- <div>
- <EmailSignUpPage/>
- </div>
- );
- } else if (this.state.page === 'ldap') {
- return (
- <div>
- {teamListing}
- <LdapSignUpPage/>
- </div>
- );
- } else if (this.state.page === 'gitlab') {
- signupMethod = (
- <SSOSignupPage service={Constants.GITLAB_SERVICE}/>
- );
- } else if (this.state.page === 'google') {
- signupMethod = (
- <SSOSignupPage service={Constants.GOOGLE_SERVICE}/>
- );
- } else if (this.state.page === 'none') {
- signupMethod = (
- <FormattedMessage
- id='signup_team.none'
- defaultMessage='No team creation method has been enabled. Please contact an administrator for access.'
- />
- );
- goBack = null;
- }
-
- return (
- <div>
- {goBack}
- <div className='col-sm-12'>
- <div className='signup-team__container'>
- <img
- className='signup-team-logo'
- src={logoImage}
- />
- <h1>{global.window.mm_config.SiteName}</h1>
- <h4 className='color--light'>
- <FormattedMessage
- id='web.root.singup_info'
- />
- </h4>
- <div id='signup-team'>
- {teamListing}
- {signupMethod}
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-TeamSignUp.propTypes = {
-};
-
diff --git a/webapp/components/signup_team_complete/components/signup_team_complete.jsx b/webapp/components/signup_team_complete/components/signup_team_complete.jsx
index 95b41dbde..00fdafe5f 100644
--- a/webapp/components/signup_team_complete/components/signup_team_complete.jsx
+++ b/webapp/components/signup_team_complete/components/signup_team_complete.jsx
@@ -74,6 +74,7 @@ export default class SignupTeamComplete extends React.Component {
SignupTeamComplete.defaultProps = {
};
+
SignupTeamComplete.propTypes = {
location: React.PropTypes.object,
children: React.PropTypes.node
diff --git a/webapp/components/signup_team_complete/components/team_signup_email_item.jsx b/webapp/components/signup_team_complete/components/team_signup_email_item.jsx
deleted file mode 100644
index c3903ca85..000000000
--- a/webapp/components/signup_team_complete/components/team_signup_email_item.jsx
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ReactDOM from 'react-dom';
-import * as Utils from 'utils/utils.jsx';
-
-import {intlShape, injectIntl, defineMessages} from 'react-intl';
-
-const holders = defineMessages({
- validEmail: {
- id: 'team_signup_email.validEmail',
- defaultMessage: 'Please enter a valid email address'
- },
- different: {
- id: 'team_signup_email.different',
- defaultMessage: 'Please use a different email than the one used at signup'
- },
- address: {
- id: 'team_signup_email.address',
- defaultMessage: 'Email Address'
- }
-});
-
-import React from 'react';
-
-class TeamSignupEmailItem extends React.Component {
- constructor(props) {
- super(props);
-
- this.getValue = this.getValue.bind(this);
- this.validate = this.validate.bind(this);
-
- this.state = {};
- }
- getValue() {
- return ReactDOM.findDOMNode(this.refs.email).value.trim();
- }
- validate(teamEmail) {
- const {formatMessage} = this.props.intl;
- const email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
-
- if (!email) {
- return true;
- }
-
- if (!Utils.isEmail(email)) {
- this.setState({emailError: formatMessage(holders.validEmail)});
- return false;
- } else if (email === teamEmail) {
- this.setState({emailError: formatMessage(holders.different)});
- return false;
- }
-
- this.setState({emailError: ''});
- return true;
- }
- render() {
- let emailError = null;
- let emailDivClass = 'form-group';
- if (this.state.emailError) {
- emailError = <label className='control-label'>{this.state.emailError}</label>;
- emailDivClass += ' has-error';
- }
-
- return (
- <div className={emailDivClass}>
- <input
- autoFocus={this.props.focus}
- type='email'
- ref='email'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.address)}
- defaultValue={this.props.email}
- maxLength='128'
- spellCheck='false'
- />
- {emailError}
- </div>
- );
- }
-}
-
-TeamSignupEmailItem.propTypes = {
- intl: intlShape.isRequired,
- focus: React.PropTypes.bool,
- email: React.PropTypes.string
-};
-
-export default injectIntl(TeamSignupEmailItem, {withRef: true});
diff --git a/webapp/components/signup_team_complete/components/team_signup_finished.jsx b/webapp/components/signup_team_complete/components/team_signup_finished.jsx
deleted file mode 100644
index 9fbb8473a..000000000
--- a/webapp/components/signup_team_complete/components/team_signup_finished.jsx
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'react-intl';
-
-import React from 'react';
-
-export default class FinishedPage extends React.Component {
- render() {
- return (
- <FormattedMessage
- id='signup_team_complete.completed'
- defaultMessage="You've already completed the signup process for this invitation or this invitation has expired."
- />
- );
- }
-}
diff --git a/webapp/components/signup_team_complete/components/team_signup_password_page.jsx b/webapp/components/signup_team_complete/components/team_signup_password_page.jsx
deleted file mode 100644
index 7b8b49e0c..000000000
--- a/webapp/components/signup_team_complete/components/team_signup_password_page.jsx
+++ /dev/null
@@ -1,225 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import $ from 'jquery';
-import ReactDOM from 'react-dom';
-import * as Client from 'utils/client.jsx';
-import BrowserStore from 'stores/browser_store.jsx';
-import UserStore from 'stores/user_store.jsx';
-import Constants from 'utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl';
-import {browserHistory} from 'react-router';
-
-import logoImage from 'images/logo.png';
-
-const holders = defineMessages({
- passwordError: {
- id: 'team_signup_password.passwordError',
- defaultMessage: 'Please enter at least {chars} characters'
- },
- creating: {
- id: 'team_signup_password.creating',
- defaultMessage: 'Creating team...'
- }
-});
-
-import React from 'react';
-
-class TeamSignupPasswordPage extends React.Component {
- constructor(props) {
- super(props);
-
- this.submitBack = this.submitBack.bind(this);
- this.submitNext = this.submitNext.bind(this);
-
- this.state = {};
- }
- submitBack(e) {
- e.preventDefault();
- this.props.state.wizard = 'username';
- this.props.updateParent(this.props.state);
- }
- submitNext(e) {
- e.preventDefault();
-
- var password = ReactDOM.findDOMNode(this.refs.password).value.trim();
- if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) {
- this.setState({passwordError: this.props.intl.formatMessage(holders.passwordError, {chars: Constants.MIN_PASSWORD_LENGTH})});
- return;
- }
-
- this.setState({passwordError: null, serverError: null});
- $('#finish-button').button('loading');
- var teamSignup = JSON.parse(JSON.stringify(this.props.state));
- teamSignup.user.password = password;
- teamSignup.user.allow_marketing = true;
- delete teamSignup.wizard;
-
- Client.createTeamFromSignup(teamSignup,
- () => {
- Client.track('signup', 'signup_team_08_complete');
-
- var props = this.props;
-
- Client.loginByEmail(
- teamSignup.team.name,
- teamSignup.team.email,
- teamSignup.user.password,
- '', // No MFA Token
- () => {
- UserStore.setLastEmail(teamSignup.team.email);
- if (this.props.hash > 0) {
- BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: 'finished'}));
- }
-
- $('#sign-up-button').button('reset');
- props.state.wizard = 'finished';
- props.updateParent(props.state, true);
-
- browserHistory.push('/' + teamSignup.team.name + '/channels/town-square');
- },
- (err) => {
- if (err.id === 'api.user.login.not_verified.app_error') {
- browserHistory.push('/should_verify_email?email=' + encodeURIComponent(teamSignup.team.email) + '&teamname=' + encodeURIComponent(teamSignup.team.name));
- } else {
- this.setState({serverError: err.message});
- $('#finish-button').button('reset');
- }
- }
- );
- },
- (err) => {
- this.setState({serverError: err.message});
- $('#finish-button').button('reset');
- }
- );
- }
- render() {
- Client.track('signup', 'signup_team_07_password');
-
- var passwordError = null;
- var passwordDivStyle = 'form-group';
- if (this.state.passwordError) {
- passwordError = <div className='form-group has-error'><label className='control-label'>{this.state.passwordError}</label></div>;
- passwordDivStyle = ' has-error';
- }
-
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- return (
- <div>
- <form>
- <img
- className='signup-team-logo'
- src={logoImage}
- />
- <h2 className='margin--less'>
- <FormattedMessage
- id='team_signup_password.yourPassword'
- defaultMessage='Your password'
- />
- </h2>
- <h5 className='color--light'>
- <FormattedMessage
- id='team_signup_password.selectPassword'
- defaultMessage="Select a password that you'll use to login with your email address:"
- />
- </h5>
- <div className='inner__content margin--extra'>
- <h5><strong>
- <FormattedMessage
- id='team_signup_password.email'
- defaultMessage='Email'
- />
- </strong></h5>
- <div className='block--gray form-group'>{this.props.state.team.email}</div>
- <div className={passwordDivStyle}>
- <div className='row'>
- <div className='col-sm-11'>
- <h5><strong>
- <FormattedMessage
- id='team_signup_password.choosePwd'
- defaultMessage='Choose your password'
- />
- </strong></h5>
- <input
- autoFocus={true}
- type='password'
- ref='password'
- className='form-control'
- placeholder=''
- maxLength='128'
- spellCheck='false'
- />
- <span className='color--light help-block'>
- <FormattedMessage
- id='team_signup_password.hint'
- defaultMessage='Passwords must contain {min} to {max} characters. Your password will be strongest if it contains a mix of symbols, numbers, and upper and lowercase characters.'
- values={{
- min: Constants.MIN_PASSWORD_LENGTH,
- max: Constants.MAX_PASSWORD_LENGTH
- }}
- />
- </span>
- </div>
- </div>
- {passwordError}
- {serverError}
- </div>
- </div>
- <div className='form-group'>
- <button
- type='submit'
- className='btn btn-primary margin--extra'
- id='finish-button'
- data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> ' + this.props.intl.formatMessage(holders.creating)}
- onClick={this.submitNext}
- >
- <FormattedMessage
- id='team_signup_password.finish'
- defaultMessage='Finish'
- />
- </button>
- </div>
- <p>
- <FormattedHTMLMessage
- id='team_signup_password.agreement'
- defaultMessage="By proceeding to create your account and use {siteName}, you agree to our <a href='/static/help/terms.html'>Terms of Service</a> and <a href='/static/help/privacy.html'>Privacy Policy</a>. If you do not agree, you cannot use {siteName}."
- values={{
- siteName: global.window.mm_config.SiteName
- }}
- />
- </p>
- <div className='margin--extra'>
- <a
- href='#'
- onClick={this.submitBack}
- >
- <FormattedMessage
- id='team_signup_password.back'
- defaultMessage='Back to previous step'
- />
- </a>
- </div>
- </form>
- </div>
- );
- }
-}
-
-TeamSignupPasswordPage.defaultProps = {
- state: {},
- hash: ''
-};
-TeamSignupPasswordPage.propTypes = {
- intl: intlShape.isRequired,
- state: React.PropTypes.object,
- hash: React.PropTypes.string,
- updateParent: React.PropTypes.func
-};
-
-export default injectIntl(TeamSignupPasswordPage);
diff --git a/webapp/components/signup_team_complete/components/team_signup_send_invites_page.jsx b/webapp/components/signup_team_complete/components/team_signup_send_invites_page.jsx
deleted file mode 100644
index db060b6b9..000000000
--- a/webapp/components/signup_team_complete/components/team_signup_send_invites_page.jsx
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import $ from 'jquery';
-import EmailItem from './team_signup_email_item.jsx';
-import * as Client from 'utils/client.jsx';
-
-import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
-
-import logoImage from 'images/logo.png';
-
-import React from 'react';
-
-export default class TeamSignupSendInvitesPage extends React.Component {
- constructor(props) {
- super(props);
- this.submitBack = this.submitBack.bind(this);
- this.submitNext = this.submitNext.bind(this);
- this.submitAddInvite = this.submitAddInvite.bind(this);
- this.submitSkip = this.submitSkip.bind(this);
- this.keySubmit = this.keySubmit.bind(this);
- this.state = {
- emailEnabled: global.window.mm_config.SendEmailNotifications === 'true'
- };
- }
- submitBack(e) {
- e.preventDefault();
- this.props.state.wizard = 'team_url';
-
- this.props.updateParent(this.props.state);
- }
- submitNext(e) {
- e.preventDefault();
-
- var valid = true;
-
- if (this.state.emailEnabled) {
- var emails = [];
-
- for (var i = 0; i < this.props.state.invites.length; i++) {
- if (this.refs['email_' + i].getWrappedInstance().validate(this.props.state.team.email)) {
- emails.push(this.refs['email_' + i].getWrappedInstance().getValue());
- } else {
- valid = false;
- }
- }
-
- if (valid) {
- this.props.state.invites = emails;
- }
- }
-
- if (valid) {
- this.props.state.wizard = 'username';
- this.props.updateParent(this.props.state);
- }
- }
- submitAddInvite(e) {
- e.preventDefault();
- this.props.state.wizard = 'send_invites';
- if (!this.props.state.invites) {
- this.props.state.invites = [];
- }
- this.props.state.invites.push('');
- this.props.updateParent(this.props.state);
- }
- submitSkip(e) {
- e.preventDefault();
- this.props.state.wizard = 'username';
- this.props.updateParent(this.props.state);
- }
- keySubmit(e) {
- if (e && e.keyCode === 13) {
- this.submitNext(e);
- }
- }
- componentDidMount() {
- if (!this.state.emailEnabled) {
- // Must use keypress not keyup due to event chain of pressing enter
- $('body').keypress(this.keySubmit);
- }
- }
- componentWillUnmount() {
- if (!this.state.emailEnabled) {
- $('body').off('keypress', this.keySubmit);
- }
- }
- render() {
- Client.track('signup', 'signup_team_05_send_invites');
-
- var content = null;
- var bottomContent = null;
-
- if (this.state.emailEnabled) {
- var emails = [];
-
- for (var i = 0; i < this.props.state.invites.length; i++) {
- if (i === 0) {
- emails.push(
- <EmailItem
- focus={true}
- key={i}
- ref={'email_' + i}
- email={this.props.state.invites[i]}
- />
- );
- } else {
- emails.push(
- <EmailItem
- focus={false}
- key={i}
- ref={'email_' + i}
- email={this.props.state.invites[i]}
- />
- );
- }
- }
-
- content = (
- <div>
- {emails}
- <div className='form-group text-right'>
- <a
- href='#'
- onClick={this.submitAddInvite}
- >
- <FormattedMessage
- id='team_signup_send_invites.addInvitation'
- defaultMessage='Add Invitation'
- />
- </a>
- </div>
- </div>
- );
-
- bottomContent = (
- <p className='color--light'>
- <FormattedHTMLMessage
- id='team_signup_send_invites.prefer'
- defaultMessage='if you prefer, you can invite team members later<br /> and '
- />
- <a
- href='#'
- onClick={this.submitSkip}
- >
- <FormattedMessage
- id='team_signup_send_invites.skip'
- defaultMessage='skip this step '
- />
- </a>
- <FormattedMessage
- id='team_signup_send_invites.forNow'
- defaultMessage='for now.'
- />
- </p>
- );
- } else {
- content = (
- <div className='form-group color--light'>
- <FormattedMessage
- id='team_signup_send_invites.disabled'
- defaultMessage='Email is currently disabled for your team, and emails cannot be sent. Contact your system administrator to enable email and email invitations.'
- />
- </div>
- );
- }
-
- return (
- <div>
- <form>
- <img
- className='signup-team-logo'
- src={logoImage}
- />
- <h2>
- <FormattedMessage
- id='team_signup_send_invites.title'
- defaultMessage='Invite Team Members'
- />
- </h2>
- {content}
- <div className='form-group'>
- <button
- type='submit'
- className='btn-primary btn'
- onClick={this.submitNext}
- >
- <FormattedMessage
- id='team_signup_send_invites.next'
- defaultMessage='Next'
- /><i className='glyphicon glyphicon-chevron-right'/>
- </button>
- </div>
- </form>
- {bottomContent}
- <div className='margin--extra'>
- <a
- href='#'
- onClick={this.submitBack}
- >
- <FormattedMessage
- id='team_signup_send_invites.back'
- defaultMessage='Back to previous step'
- />
- </a>
- </div>
- </div>
- );
- }
-}
-
-TeamSignupSendInvitesPage.propTypes = {
- state: React.PropTypes.object.isRequired,
- updateParent: React.PropTypes.func.isRequired
-};
diff --git a/webapp/components/signup_team_complete/components/team_signup_username_page.jsx b/webapp/components/signup_team_complete/components/team_signup_username_page.jsx
deleted file mode 100644
index b79c1179f..000000000
--- a/webapp/components/signup_team_complete/components/team_signup_username_page.jsx
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ReactDOM from 'react-dom';
-import * as Utils from 'utils/utils.jsx';
-import * as Client from 'utils/client.jsx';
-import Constants from 'utils/constants.jsx';
-
-import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-intl';
-
-import logoImage from 'images/logo.png';
-
-const holders = defineMessages({
- reserved: {
- id: 'team_signup_username.reserved',
- defaultMessage: 'This username is reserved, please choose a new one.'
- },
- invalid: {
- id: 'team_signup_username.invalid',
- defaultMessage: 'Username must begin with a letter, and contain between {min} to {max} characters in total, which may be numbers, lowercase letters, or any of the symbols \'.\', \'-\', or \'_\''
- }
-});
-
-import React from 'react';
-
-class TeamSignupUsernamePage extends React.Component {
- constructor(props) {
- super(props);
-
- this.submitBack = this.submitBack.bind(this);
- this.submitNext = this.submitNext.bind(this);
-
- this.state = {};
- }
- submitBack(e) {
- e.preventDefault();
- if (global.window.mm_config.SendEmailNotifications === 'true') {
- this.props.state.wizard = 'send_invites';
- } else {
- this.props.state.wizard = 'team_url';
- }
-
- this.props.updateParent(this.props.state);
- }
- submitNext(e) {
- e.preventDefault();
-
- const {formatMessage} = this.props.intl;
- var name = ReactDOM.findDOMNode(this.refs.name).value.trim().toLowerCase();
-
- var usernameError = Utils.isValidUsername(name);
- if (usernameError === 'Cannot use a reserved word as a username.') { //this should be change to some kind of ID
- this.setState({nameError: formatMessage(holders.reserved)});
- return;
- } else if (usernameError) {
- this.setState({nameError: formatMessage(holders.invalid, {min: Constants.MIN_USERNAME_LENGTH, max: Constants.MAX_USERNAME_LENGTH})});
- return;
- }
-
- this.props.state.wizard = 'password';
- this.props.state.user.username = name;
- this.props.updateParent(this.props.state);
- }
- render() {
- Client.track('signup', 'signup_team_06_username');
-
- var nameError = null;
- var nameHelpText = (
- <span className='color--light help-block'>
- <FormattedMessage
- id='team_signup_username.hint'
- defaultMessage="Usernames must begin with a letter and contain between {min} to {max} characters made up of lowercase letters, numbers, and the symbols '.', '-' and '_'"
- values={{
- min: Constants.MIN_USERNAME_LENGTH,
- max: Constants.MAX_USERNAME_LENGTH
- }}
- />
- </span>
- );
- var nameDivClass = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameHelpText = '';
- nameDivClass += ' has-error';
- }
-
- return (
- <div>
- <form>
- <img
- className='signup-team-logo'
- src={logoImage}
- />
- <h2 className='margin--less'>
- <FormattedMessage
- id='team_signup_username.username'
- defaultMessage='Your username'
- />
- </h2>
- <h5 className='color--light'>
- <FormattedMessage
- id='team_signup_username.memorable'
- defaultMessage='Select a memorable username that makes it easy for teammates to identify you:'
- />
- </h5>
- <div className='inner__content margin--extra'>
- <div className={nameDivClass}>
- <div className='row'>
- <div className='col-sm-11'>
- <h5><strong>
- <FormattedMessage
- id='team_signup_username.chooseUsername'
- defaultMessage='Choose your username'
- />
- </strong></h5>
- <input
- autoFocus={true}
- type='text'
- ref='name'
- className='form-control'
- placeholder=''
- defaultValue={this.props.state.user.username}
- maxLength={Constants.MAX_USERNAME_LENGTH}
- spellCheck='false'
- />
- {nameHelpText}
- </div>
- </div>
- {nameError}
- </div>
- </div>
- <button
- type='submit'
- className='btn btn-primary margin--extra'
- onClick={this.submitNext}
- >
- <FormattedMessage
- id='team_signup_username.next'
- defaultMessage='Next'
- />
- <i className='glyphicon glyphicon-chevron-right'></i>
- </button>
- <div className='margin--extra'>
- <a
- href='#'
- onClick={this.submitBack}
- >
- <FormattedMessage
- id='team_signup_username.back'
- defaultMessage='Back to previous step'
- />
- </a>
- </div>
- </form>
- </div>
- );
- }
-}
-
-TeamSignupUsernamePage.defaultProps = {
- state: null
-};
-TeamSignupUsernamePage.propTypes = {
- intl: intlShape.isRequired,
- state: React.PropTypes.object,
- updateParent: React.PropTypes.func
-};
-
-export default injectIntl(TeamSignupUsernamePage);
diff --git a/webapp/components/signup_team_complete/components/team_signup_welcome_page.jsx b/webapp/components/signup_team_complete/components/team_signup_welcome_page.jsx
deleted file mode 100644
index 15b708128..000000000
--- a/webapp/components/signup_team_complete/components/team_signup_welcome_page.jsx
+++ /dev/null
@@ -1,243 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ReactDOM from 'react-dom';
-import * as Utils from 'utils/utils.jsx';
-import * as Client from 'utils/client.jsx';
-import BrowserStore from 'stores/browser_store.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl';
-
-import {browserHistory} from 'react-router';
-
-import logoImage from 'images/logo.png';
-
-const holders = defineMessages({
- storageError: {
- id: 'team_signup_welcome.storageError',
- defaultMessage: 'This service requires local storage to be enabled. Please enable it or exit private browsing.'
- },
- validEmailError: {
- id: 'team_signup_welcome.validEmailError',
- defaultMessage: 'Please enter a valid email address'
- },
- address: {
- id: 'team_signup_welcome.address',
- defaultMessage: 'Email Address'
- }
-});
-
-import React from 'react';
-
-class TeamSignupWelcomePage extends React.Component {
- constructor(props) {
- super(props);
-
- this.submitNext = this.submitNext.bind(this);
- this.handleDiffEmail = this.handleDiffEmail.bind(this);
- this.handleDiffSubmit = this.handleDiffSubmit.bind(this);
- this.handleKeyPress = this.handleKeyPress.bind(this);
-
- this.state = {useDiff: false};
-
- document.addEventListener('keyup', this.handleKeyPress, false);
- }
- submitNext(e) {
- if (!BrowserStore.isLocalStorageSupported()) {
- this.setState({storageError: this.props.intl.formatMessage(holders.storageError)});
- return;
- }
- e.preventDefault();
- this.props.state.wizard = 'team_display_name';
- this.props.updateParent(this.props.state);
- }
- handleDiffEmail(e) {
- e.preventDefault();
- this.setState({useDiff: true});
- }
- handleDiffSubmit(e) {
- e.preventDefault();
-
- const {formatMessage} = this.props.intl;
- var state = {useDiff: true, serverError: ''};
-
- var email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
- if (!email || !Utils.isEmail(email)) {
- state.emailError = formatMessage(holders.validEmailError);
- this.setState(state);
- return;
- } else if (!BrowserStore.isLocalStorageSupported()) {
- state.emailError = formatMessage(holders.storageError);
- this.setState(state);
- return;
- }
- state.emailError = '';
-
- Client.signupTeam(email,
- function success(data) {
- if (data.follow_link) {
- browserHistory.push(data.follow_link);
- } else {
- this.props.state.wizard = 'finished';
- this.props.updateParent(this.props.state);
- browserHistory.push('/signup_team_confirm/?email=' + encodeURIComponent(email));
- }
- }.bind(this),
- function error(err) {
- let errorMsg = err.message;
-
- if (err.detailed_error.indexOf('Invalid RCPT TO address provided') >= 0) {
- errorMsg = formatMessage(holders.validEmailError);
- }
-
- this.setState({emailError: '', serverError: errorMsg});
- }.bind(this)
- );
- }
- handleKeyPress(event) {
- if (event.keyCode === 13) {
- if (this.state.useDiff) {
- this.handleDiffSubmit(event);
- } else {
- this.submitNext(event);
- }
- }
- }
- componentWillUnmount() {
- document.removeEventListener('keyup', this.handleKeyPress, false);
- }
- render() {
- Client.track('signup', 'signup_team_01_welcome');
-
- var storageError = null;
- if (this.state.storageError) {
- storageError = <label className='control-label'>{this.state.storageError}</label>;
- }
-
- var emailError = null;
- var emailDivClass = 'form-group';
- if (this.state.emailError) {
- emailError = <label className='control-label'>{this.state.emailError}</label>;
- emailDivClass += ' has-error';
- }
-
- var serverError = null;
- if (this.state.serverError) {
- serverError = (
- <div className='form-group has-error'>
- <label className='control-label'>{this.state.serverError}</label>
- </div>
- );
- }
-
- var differentEmailLinkClass = '';
- var emailDivContainerClass = 'hidden';
- if (this.state.useDiff) {
- differentEmailLinkClass = 'hidden';
- emailDivContainerClass = '';
- }
-
- return (
- <div>
- <img
- className='signup-team-logo'
- src={logoImage}
- />
- <h3 className='sub-heading'>
- <FormattedMessage
- id='team_signup_welcome.welcome'
- defaultMessage='Welcome to:'
- />
- </h3>
- <h1 className='margin--top-none'>{global.window.mm_config.SiteName}</h1>
- <p className='margin--less'>
- <FormattedMessage
- id='team_signup_welcome.lets'
- defaultMessage="Let's set up your new team"
- />
- </p>
- <div>
- <FormattedMessage
- id='team_signup_welcome.confirm'
- defaultMessage='Please confirm your email address:'
- />
- <br/>
- <div className='inner__content'>
- <div className='block--gray'>{this.props.state.team.email}</div>
- </div>
- </div>
- <p className='margin--extra color--light'>
- <FormattedHTMLMessage
- id='team_signup_welcome.admin'
- defaultMessage='Your account will administer the new team site. <br />
- You can add other administrators later.'
- />
- </p>
- <div className='form-group'>
- <button
- className='btn-primary btn form-group'
- type='submit'
- onClick={this.submitNext}
- >
- <i className='glyphicon glyphicon-ok'></i>
- <FormattedMessage
- id='team_signup_welcome.yes'
- defaultMessage='Yes, this address is correct'
- />
- </button>
- {storageError}
- </div>
- <hr/>
- <div className={emailDivContainerClass}>
- <div className={emailDivClass}>
- <div className='row'>
- <div className='col-sm-9'>
- <input
- type='email'
- ref='email'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.address)}
- maxLength='128'
- spellCheck='false'
- />
- </div>
- </div>
- {emailError}
- </div>
- {serverError}
- <button
- className='btn btn-md btn-primary'
- type='button'
- onClick={this.handleDiffSubmit}
- >
- <FormattedMessage
- id='team_signup_welcome.instead'
- defaultMessage='Use this instead'
- />
- </button>
- </div>
- <a
- href='#'
- onClick={this.handleDiffEmail}
- className={differentEmailLinkClass}
- >
- <FormattedMessage
- id='team_signup_welcome.different'
- defaultMessage='Use a different email'
- />
- </a>
- </div>
- );
- }
-}
-
-TeamSignupWelcomePage.defaultProps = {
- state: {}
-};
-TeamSignupWelcomePage.propTypes = {
- intl: intlShape.isRequired,
- updateParent: React.PropTypes.func.isRequired,
- state: React.PropTypes.object
-};
-
-export default injectIntl(TeamSignupWelcomePage);
diff --git a/webapp/components/signup_team_confirm.jsx b/webapp/components/signup_team_confirm.jsx
deleted file mode 100644
index 117a0b068..000000000
--- a/webapp/components/signup_team_confirm.jsx
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
-import {Link} from 'react-router';
-
-import React from 'react';
-
-export default class SignupTeamConfirm extends React.Component {
- render() {
- return (
- <div>
- <div className='signup-header'>
- <Link to='/'>
- <span className='fa fa-chevron-left'/>
- <FormattedMessage
- id='web.header.back'
- />
- </Link>
- </div>
- <div className='col-sm-12'>
- <div classNameName='signup-team__container'>
- <h3>
- <FormattedMessage
- id='signup_team_confirm.title'
- defaultMessage='Sign up Complete'
- />
- </h3>
- <p>
- <FormattedHTMLMessage
- id='signup_team_confirm.checkEmail'
- defaultMessage='Please check your email: <strong>{email}</strong><br />Your email contains a link to set up your team'
- values={{
- email: this.props.location.query.email
- }}
- />
- </p>
- </div>
- </div>
- </div>
- );
- }
-}
-
-SignupTeamConfirm.defaultProps = {
-};
-SignupTeamConfirm.propTypes = {
- location: React.PropTypes.object
-};
diff --git a/webapp/components/signup_user_complete.jsx b/webapp/components/signup_user_complete.jsx
index 9e821289b..666e72e13 100644
--- a/webapp/components/signup_user_complete.jsx
+++ b/webapp/components/signup_user_complete.jsx
@@ -3,12 +3,13 @@
import LoadingScreen from 'components/loading_screen.jsx';
import LoginLdap from 'components/login/components/login_ldap.jsx';
+import * as GlobalActions from 'action_creators/global_actions.jsx';
import BrowserStore from 'stores/browser_store.jsx';
import UserStore from 'stores/user_store.jsx';
import * as Utils from 'utils/utils.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
@@ -24,7 +25,6 @@ class SignupUserComplete extends React.Component {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
- this.inviteInfoRecieved = this.inviteInfoRecieved.bind(this);
this.handleLdapSignup = this.handleLdapSignup.bind(this);
this.state = {
@@ -34,7 +34,10 @@ class SignupUserComplete extends React.Component {
email: '',
teamDisplayName: '',
teamName: '',
- teamId: ''
+ teamId: '',
+ openServer: false,
+ loading: true,
+ inviteId: ''
};
}
componentWillMount() {
@@ -46,19 +49,91 @@ class SignupUserComplete extends React.Component {
let teamDisplayName = '';
let teamName = '';
let teamId = '';
+ let openServer = false;
+ let loading = true;
+
+ if ((inviteId && inviteId.length > 0) || (hash && hash.length > 0)) {
+ // if we are already logged in then attempt to just join the team
+ if (UserStore.getCurrentUser()) {
+ loading = true;
+ Client.addUserToTeamFromInvite(
+ data,
+ hash,
+ inviteId,
+ () => {
+ GlobalActions.emitInitialLoad(
+ () => {
+ browserHistory.push('/select_team');
+ }
+ );
+ },
+ (err) => {
+ this.setState({
+ noOpenServerError: true,
+ serverError: err.message,
+ loading: false
+ });
+ }
+ );
+ } else if (hash) {
+ // If we have a hash in the url then we are attempting to access a private team
+ const parsedData = JSON.parse(data);
+ usedBefore = BrowserStore.getGlobalItem(hash);
+ email = parsedData.email;
+ teamDisplayName = parsedData.display_name;
+ teamName = parsedData.name;
+ teamId = parsedData.id;
+ loading = false;
+ } else {
+ loading = true;
+ Client.getInviteInfo(
+ inviteId,
+ (inviteData) => {
+ if (!inviteData) {
+ return;
+ }
- // If we have a hash in the url then we are attempting to access a private team
- if (hash) {
- const parsedData = JSON.parse(data);
- usedBefore = BrowserStore.getGlobalItem(hash);
- email = parsedData.email;
- teamDisplayName = parsedData.display_name;
- teamName = parsedData.name;
- teamId = parsedData.id;
+ this.setState({
+ serverError: null,
+ teamDisplayName: inviteData.display_name,
+ teamName: inviteData.name,
+ teamId: inviteData.id,
+ loading: false
+ });
+ },
+ () => {
+ this.setState({
+ noOpenServerError: true,
+ loading: false,
+ serverError:
+ <FormattedMessage
+ id='signup_user_completed.invalid_invite'
+ defaultMessage='The invite link was invalid. Please speak with your Administrator to receive an invitation.'
+ />
+ });
+ }
+ );
+
+ data = '';
+ hash = '';
+ }
+ } else if (global.window.mm_config.EnableOpenServer === 'true' || UserStore.getNoAccounts()) {
+ // If this is the first account then let them create an account anyway.
+ // The server will verify it's the first account before allowing creation.
+ // Of if the server is open then we don't care.
+ openServer = true;
+ loading = false;
} else {
- Client.getInviteInfo(this.inviteInfoRecieved, null, inviteId);
- data = '';
- hash = '';
+ loading = false;
+ this.setState({
+ noOpenServerError: true,
+ serverError:
+ <FormattedMessage
+ id='signup_user_completed.no_open_server'
+ defaultMessage='This server does not allow open signups. Please speak with your Administrator to receive an invitation.'
+ />,
+ loading: false
+ });
}
this.setState({
@@ -68,29 +143,25 @@ class SignupUserComplete extends React.Component {
email,
teamDisplayName,
teamName,
- teamId
- });
- }
- inviteInfoRecieved(data) {
- if (!data) {
- return;
- }
-
- this.setState({
- teamDisplayName: data.display_name,
- teamName: data.name,
- teamId: data.id
+ teamId,
+ openServer,
+ inviteId,
+ loading
});
}
handleLdapSignup(method, loginId, password, token) {
- Client.loginByLdap(this.state.teamName, loginId, password, token,
+ Client.loginByLdap(loginId, password, token,
() => {
const redirect = Utils.getUrlParameter('redirect');
if (redirect) {
browserHistory.push(decodeURIComponent(redirect));
} else {
- browserHistory.push('/' + this.state.teamName + '/channels/town-square');
+ GlobalActions.emitInitialLoad(
+ () => {
+ browserHistory.push('/select_team');
+ }
+ );
}
},
(err) => {
@@ -187,28 +258,34 @@ class SignupUserComplete extends React.Component {
});
const user = {
- team_id: this.state.teamId,
email: providedEmail,
username: providedUsername,
password: providedPassword,
allow_marketing: true
};
- Client.createUser(user, this.state.data, this.state.hash,
+ Client.createUserWithInvite(user,
+ this.state.data,
+ this.state.hash,
+ this.state.inviteId,
() => {
Client.track('signup', 'signup_user_02_complete');
-
- Client.loginByEmail(
- this.state.teamName,
+ Client.login(
user.email,
+ null,
user.password,
- '', // No MFA Token
+ '',
() => {
UserStore.setLastEmail(user.email);
if (this.state.hash > 0) {
BrowserStore.setGlobalItem(this.state.hash, JSON.stringify({usedBefore: true}));
}
- browserHistory.push('/' + this.state.teamName + '/channels/town-square');
+
+ GlobalActions.emitInitialLoad(
+ () => {
+ browserHistory.push('/select_team');
+ }
+ );
},
(err) => {
if (err.id === 'api.user.login.not_verified.app_error') {
@@ -239,9 +316,7 @@ class SignupUserComplete extends React.Component {
);
}
- // If we haven't got a team id yet we are waiting for
- // the client so just show the standard loading screen
- if (this.state.teamId === '') {
+ if (this.state.loading) {
return (<LoadingScreen/>);
}
@@ -349,7 +424,7 @@ class SignupUserComplete extends React.Component {
<a
className='btn btn-custom-login gitlab'
key='gitlab'
- href={'/api/v1/oauth/gitlab/signup' + window.location.search + '&team=' + encodeURIComponent(this.state.teamName)}
+ href={Client.getOAuthRoute() + '/gitlab/signup' + window.location.search}
>
<span className='icon'/>
<span>
@@ -367,7 +442,7 @@ class SignupUserComplete extends React.Component {
<a
className='btn btn-custom-login google'
key='google'
- href={'/api/v1/oauth/google/signup' + window.location.search + '&team=' + encodeURIComponent(this.state.teamName)}
+ href={Client.getOAuthRoute() + '/google/signup' + window.location.search + '&team=' + encodeURIComponent(this.state.teamName)}
>
<span className='icon'/>
<span>
@@ -497,12 +572,33 @@ class SignupUserComplete extends React.Component {
);
}
+ let terms = (
+ <p>
+ <FormattedHTMLMessage
+ id='create_team.agreement'
+ defaultMessage="By proceeding to create your account and use {siteName}, you agree to our <a href='/static/help/terms.html'>Terms of Service</a> and <a href='/static/help/privacy.html'>Privacy Policy</a>. If you do not agree, you cannot use {siteName}."
+ values={{
+ siteName: global.window.mm_config.SiteName
+ }}
+ />
+ </p>
+ );
+
+ if (this.state.noOpenServerError) {
+ signupMessage = null;
+ ldapSignup = null;
+ emailSignup = null;
+ terms = null;
+ }
+
return (
<div>
<div className='signup-header'>
<Link to='/'>
- <span classNameNameName='fa fa-chevron-left'/>
- <FormattedMessage id='web.header.back'/>
+ <span className='fa fa-chevron-left'/>
+ <FormattedMessage
+ id='web.header.back'
+ />
</Link>
</div>
<div className='col-sm-12'>
@@ -511,22 +607,12 @@ class SignupUserComplete extends React.Component {
className='signup-team-logo'
src={logoImage}
/>
- <h5 className='margin--less'>
- <FormattedMessage
- id='signup_user_completed.welcome'
- defaultMessage='Welcome to:'
- />
- </h5>
- <h2 className='signup-team__name'>{this.state.teamName}</h2>
- <h2 className='signup-team__subdomain'>
+ <h1>{global.window.mm_config.SiteName}</h1>
+ <h4 className='color--light'>
<FormattedMessage
- id='signup_user_completed.onSite'
- defaultMessage='on {siteName}'
- values={{
- siteName: global.window.mm_config.SiteName
- }}
+ id='web.root.singup_info'
/>
- </h2>
+ </h4>
<h4 className='color--light'>
<FormattedMessage
id='signup_user_completed.lets'
@@ -537,6 +623,7 @@ class SignupUserComplete extends React.Component {
{ldapSignup}
{emailSignup}
{serverError}
+ {terms}
</div>
</div>
</div>
diff --git a/webapp/components/suggestion/at_mention_provider.jsx b/webapp/components/suggestion/at_mention_provider.jsx
index 90ec6e660..79ac8aaf1 100644
--- a/webapp/components/suggestion/at_mention_provider.jsx
+++ b/webapp/components/suggestion/at_mention_provider.jsx
@@ -4,6 +4,7 @@
import SuggestionStore from 'stores/suggestion_store.jsx';
import UserStore from 'stores/user_store.jsx';
import * as Utils from 'utils/utils.jsx';
+import Client from 'utils/web_client.jsx';
import {FormattedMessage} from 'react-intl';
@@ -42,7 +43,7 @@ class AtMentionSuggestion extends React.Component {
icon = (
<img
className='mention__image'
- src={'/api/v1/users/' + item.id + '/image?time=' + item.update_at}
+ src={Client.getUsersRoute() + '/' + item.id + '/image?time=' + item.update_at}
/>
);
}
diff --git a/webapp/components/suggestion/search_user_provider.jsx b/webapp/components/suggestion/search_user_provider.jsx
index eeaee68a7..b7234469a 100644
--- a/webapp/components/suggestion/search_user_provider.jsx
+++ b/webapp/components/suggestion/search_user_provider.jsx
@@ -3,6 +3,7 @@
import SuggestionStore from 'stores/suggestion_store.jsx';
import UserStore from 'stores/user_store.jsx';
+import Client from 'utils/web_client.jsx';
import React from 'react';
@@ -22,7 +23,7 @@ class SearchUserSuggestion extends React.Component {
>
<img
className='profile-img rounded'
- src={'/api/v1/users/' + item.id + '/image?time=' + item.update_at}
+ src={Client.getUsersRoute() + '/' + item.id + '/image?time=' + item.update_at}
/>
<i className='fa fa fa-plus-square'></i>{item.username}
</div>
diff --git a/webapp/components/team_export_tab.jsx b/webapp/components/team_export_tab.jsx
index 9bd5785a0..37f886aab 100644
--- a/webapp/components/team_export_tab.jsx
+++ b/webapp/components/team_export_tab.jsx
@@ -1,7 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import {FormattedMessage} from 'react-intl';
diff --git a/webapp/components/team_general_tab.jsx b/webapp/components/team_general_tab.jsx
index c27e8ca59..1f783fe9f 100644
--- a/webapp/components/team_general_tab.jsx
+++ b/webapp/components/team_general_tab.jsx
@@ -5,7 +5,7 @@ import $ from 'jquery';
import SettingItemMin from './setting_item_min.jsx';
import SettingItemMax from './setting_item_max.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as Utils from 'utils/utils.jsx';
import TeamStore from 'stores/team_store.jsx';
@@ -42,7 +42,7 @@ const holders = defineMessages({
},
openInviteTitle: {
id: 'general_tab.openInviteTitle',
- defaultMessage: 'Allow anyone to sign-up from login page'
+ defaultMessage: 'Allow anyone to join this team'
},
codeTitle: {
id: 'general_tab.codeTitle',
@@ -68,7 +68,6 @@ class GeneralTab extends React.Component {
this.handleNameSubmit = this.handleNameSubmit.bind(this);
this.handleInviteIdSubmit = this.handleInviteIdSubmit.bind(this);
this.handleOpenInviteSubmit = this.handleOpenInviteSubmit.bind(this);
- this.handleTeamListingSubmit = this.handleTeamListingSubmit.bind(this);
this.handleClose = this.handleClose.bind(this);
this.onUpdateNameSection = this.onUpdateNameSection.bind(this);
this.updateName = this.updateName.bind(this);
@@ -76,8 +75,6 @@ class GeneralTab extends React.Component {
this.updateInviteId = this.updateInviteId.bind(this);
this.onUpdateOpenInviteSection = this.onUpdateOpenInviteSection.bind(this);
this.handleOpenInviteRadio = this.handleOpenInviteRadio.bind(this);
- this.onUpdateTeamListingSection = this.onUpdateTeamListingSection.bind(this);
- this.handleTeamListingRadio = this.handleTeamListingRadio.bind(this);
this.handleGenerateInviteId = this.handleGenerateInviteId.bind(this);
this.state = this.setupInitialState(props);
@@ -96,12 +93,19 @@ class GeneralTab extends React.Component {
name: team.display_name,
invite_id: team.invite_id,
allow_open_invite: team.allow_open_invite,
- allow_team_listing: team.allow_team_listing,
serverError: '',
clientError: ''
};
}
+ componentWillReceiveProps(nextProps) {
+ this.setState({
+ name: nextProps.team.display_name,
+ invite_id: nextProps.team.invite_id,
+ allow_open_invite: nextProps.team.allow_open_invite
+ });
+ }
+
handleGenerateInviteId(e) {
e.preventDefault();
@@ -117,14 +121,6 @@ class GeneralTab extends React.Component {
this.setState({allow_open_invite: openInvite});
}
- handleTeamListingRadio(listing) {
- if (global.window.mm_config.EnableTeamListing !== 'true' && listing) {
- this.setState({clientError: this.props.intl.formatMessage(holders.dirDisabled)});
- } else {
- this.setState({allow_team_listing: listing});
- }
- }
-
handleOpenInviteSubmit(e) {
e.preventDefault();
@@ -145,26 +141,6 @@ class GeneralTab extends React.Component {
);
}
- handleTeamListingSubmit(e) {
- e.preventDefault();
-
- var state = {serverError: '', clientError: ''};
-
- var data = this.props.team;
- data.allow_team_listing = this.state.allow_team_listing;
- Client.updateTeam(data,
- (team) => {
- TeamStore.saveTeam(team);
- TeamStore.emitChange();
- this.updateSection('');
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
-
handleNameSubmit(e) {
e.preventDefault();
@@ -239,12 +215,6 @@ class GeneralTab extends React.Component {
);
}
- componentWillReceiveProps(newProps) {
- if (newProps.team && newProps.teamDisplayName) {
- this.setState({name: newProps.teamDisplayName});
- }
- }
-
handleClose() {
this.updateSection('');
}
@@ -284,15 +254,6 @@ class GeneralTab extends React.Component {
}
}
- onUpdateTeamListingSection(e) {
- e.preventDefault();
- if (this.props.activeSection === 'team_listing') {
- this.updateSection('');
- } else {
- this.updateSection('team_listing');
- }
- }
-
updateName(e) {
e.preventDefault();
this.setState({name: e.target.value});
@@ -313,105 +274,8 @@ class GeneralTab extends React.Component {
serverError = this.state.serverError;
}
- const enableTeamListing = global.window.mm_config.EnableTeamListing === 'true';
const {formatMessage} = this.props.intl;
- let teamListingSection;
- if (this.props.activeSection === 'team_listing') {
- const inputs = [];
- let submitHandle = null;
-
- if (enableTeamListing) {
- submitHandle = this.handleTeamListingSubmit;
-
- inputs.push(
- <div key='userTeamListingOptions'>
- <div className='radio'>
- <label>
- <input
- name='userTeamListingOptions'
- type='radio'
- defaultChecked={this.state.allow_team_listing}
- onChange={this.handleTeamListingRadio.bind(this, true)}
- />
- <FormattedMessage
- id='general_tab.yes'
- defaultMessage='Yes'
- />
- </label>
- <br/>
- </div>
- <div className='radio'>
- <label>
- <input
- ref='teamListingRadioNo'
- name='userTeamListingOptions'
- type='radio'
- defaultChecked={!this.state.allow_team_listing}
- onChange={this.handleTeamListingRadio.bind(this, false)}
- />
- <FormattedMessage
- id='general_tab.no'
- defaultMessage='No'
- />
- </label>
- <br/>
- </div>
- <div>
- <br/>
- <FormattedMessage
- id='general_tab.includeDirDesc'
- defaultMessage='Including this team will display the team name from the Team Directory section of the Home Page, and provide a link to the sign-in page.'
- />
- </div>
- </div>
- );
- } else {
- inputs.push(
- <div key='userTeamListingOptions'>
- <div>
- <br/>
- <FormattedMessage
- id='general_tab.dirContact'
- defaultMessage='Contact your system administrator to turn on the team directory on the system home page.'
- />
- </div>
- </div>
- );
- }
-
- teamListingSection = (
- <SettingItemMax
- title={formatMessage(holders.includeDirTitle)}
- inputs={inputs}
- submit={submitHandle}
- server_error={serverError}
- client_error={clientError}
- updateSection={this.onUpdateTeamListingSection}
- />
- );
- } else {
- let describe = '';
-
- if (enableTeamListing) {
- if (this.state.allow_team_listing === true) {
- describe = formatMessage(holders.yes);
- } else {
- describe = formatMessage(holders.no);
- }
- } else {
- describe = formatMessage(holders.dirOff);
- }
-
- teamListingSection = (
- <SettingItemMin
- title={formatMessage(holders.includeDirTitle)}
- describe={describe}
- updateSection={this.onUpdateTeamListingSection}
- />
- );
- }
-
let openInviteSection;
if (this.props.activeSection === 'open_invite') {
const inputs = [
@@ -450,7 +314,7 @@ class GeneralTab extends React.Component {
<br/>
<FormattedMessage
id='general_tab.openInviteDesc'
- defaultMessage='When allowed, a link to account creation will be included on the sign-in page of this team and allow any visitor to sign-up.'
+ defaultMessage='When allowed, a link to this team will be including on the landing page allowing anyone with an account to join this team.'
/>
</div>
</div>
@@ -639,8 +503,6 @@ class GeneralTab extends React.Component {
<div className='divider-light'/>
{openInviteSection}
<div className='divider-light'/>
- {teamListingSection}
- <div className='divider-light'/>
{inviteSection}
<div className='divider-dark'/>
</div>
diff --git a/webapp/components/team_members_dropdown.jsx b/webapp/components/team_members_dropdown.jsx
index ad82a2280..251c2ce3b 100644
--- a/webapp/components/team_members_dropdown.jsx
+++ b/webapp/components/team_members_dropdown.jsx
@@ -3,7 +3,7 @@
import UserStore from 'stores/user_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx';
import ConfirmModal from './confirm_modal.jsx';
@@ -38,11 +38,9 @@ export default class TeamMembersDropdown extends React.Component {
if (this.props.user.id === me.id) {
this.handleDemote(this.props.user, '');
} else {
- const data = {
- user_id: this.props.user.id,
- new_roles: ''
- };
- Client.updateRoles(data,
+ Client.updateRoles(
+ this.props.user.id,
+ '',
() => {
AsyncClient.getProfiles();
},
@@ -79,12 +77,9 @@ export default class TeamMembersDropdown extends React.Component {
if (this.props.user.id === me.id) {
this.handleDemote(this.props.user, 'admin');
} else {
- const data = {
- user_id: this.props.user.id,
- new_roles: 'admin'
- };
-
- Client.updateRoles(data,
+ Client.updateRoles(
+ this.props.user.id,
+ 'admin',
() => {
AsyncClient.getProfiles();
},
@@ -94,12 +89,13 @@ export default class TeamMembersDropdown extends React.Component {
);
}
}
- handleDemote(user, role) {
+ handleDemote(user, role, newRole) {
this.setState({
serverError: this.state.serverError,
showDemoteModal: true,
user,
- role
+ role,
+ newRole
});
}
handleDemoteCancel() {
@@ -107,16 +103,14 @@ export default class TeamMembersDropdown extends React.Component {
serverError: null,
showDemoteModal: false,
user: null,
- role: null
+ role: null,
+ newRole: null
});
}
handleDemoteSubmit() {
- const data = {
- user_id: this.props.user.id,
- new_roles: this.state.role
- };
-
- Client.updateRoles(data,
+ Client.updateRoles(
+ this.props.user.id,
+ this.state.newRole,
() => {
const teamUrl = TeamStore.getCurrentTeamUrl();
if (teamUrl) {
@@ -140,6 +134,7 @@ export default class TeamMembersDropdown extends React.Component {
);
}
+ const teamMember = this.props.teamMember;
const user = this.props.user;
let currentRoles = (
<FormattedMessage
@@ -168,8 +163,10 @@ export default class TeamMembersDropdown extends React.Component {
}
}
- let showMakeMember = user.roles === 'admin' || user.roles === 'system_admin';
- let showMakeAdmin = user.roles === '' || user.roles === 'system_admin';
+ let showMakeMember = teamMember.roles === 'admin' || user.roles === 'system_admin';
+
+ //let showMakeAdmin = teamMember.roles === '' && user.roles !== 'system_admin';
+ let showMakeAdmin = false;
let showMakeActive = false;
let showMakeNotActive = user.roles !== 'system_admin';
@@ -331,5 +328,6 @@ export default class TeamMembersDropdown extends React.Component {
}
TeamMembersDropdown.propTypes = {
- user: React.PropTypes.object.isRequired
+ user: React.PropTypes.object.isRequired,
+ teamMember: React.PropTypes.object.isRequired
};
diff --git a/webapp/components/team_settings.jsx b/webapp/components/team_settings.jsx
index dc303059d..210d1f541 100644
--- a/webapp/components/team_settings.jsx
+++ b/webapp/components/team_settings.jsx
@@ -25,6 +25,7 @@ export default class TeamSettings extends React.Component {
}
onChange() {
var team = TeamStore.getCurrent();
+
if (!Utils.areObjectsEqual(this.state.team, team)) {
this.setState({team});
}
diff --git a/webapp/components/team_signup_choose_auth.jsx b/webapp/components/team_signup_choose_auth.jsx
deleted file mode 100644
index 7a4ce972a..000000000
--- a/webapp/components/team_signup_choose_auth.jsx
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'react-intl';
-
-import React from 'react';
-
-export default class ChooseAuthPage extends React.Component {
- constructor(props) {
- super(props);
- this.state = {};
- }
- render() {
- var buttons = [];
- if (global.window.mm_config.EnableSignUpWithGitLab === 'true') {
- buttons.push(
- <a
- className='btn btn-custom-login gitlab btn-full'
- key='gitlab'
- href='#'
- onClick={
- function clickGit(e) {
- e.preventDefault();
- this.props.updatePage('gitlab');
- }.bind(this)
- }
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='choose_auth_page.gitlabCreate'
- defaultMessage='Create new team with GitLab Account'
- />
- </span>
- </a>
- );
- }
-
- if (global.window.mm_config.EnableSignUpWithGoogle === 'true') {
- buttons.push(
- <a
- className='btn btn-custom-login google btn-full'
- key='google'
- href='#'
- onClick={
- (e) => {
- e.preventDefault();
- this.props.updatePage('google');
- }
- }
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='choose_auth_page.googleCreate'
- defaultMessage='Create new team with Google Apps Account'
- />
- </span>
- </a>
- );
- }
-
- if (global.window.mm_config.EnableLdap === 'true') {
- buttons.push(
- <a
- className='btn btn-custom-login ldap btn-full'
- key='ldap'
- href='#'
- onClick={
- (e) => {
- e.preventDefault();
- this.props.updatePage('ldap');
- }
- }
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='choose_auth_page.ldapCreate'
- defaultMessage='Create new team with LDAP Account'
- />
- </span>
- </a>
- );
- }
-
- if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
- buttons.push(
- <a
- className='btn btn-custom-login email btn-full'
- key='email'
- href='#'
- onClick={
- function clickEmail(e) {
- e.preventDefault();
- this.props.updatePage('email');
- }.bind(this)
- }
- >
- <span className='fa fa-envelope'/>
- <span>
- <FormattedMessage
- id='choose_auth_page.emailCreate'
- defaultMessage='Create new team with email address'
- />
- </span>
- </a>
- );
- }
-
- if (buttons.length === 0) {
- buttons = (
- <span>
- <FormattedMessage
- id='choose_auth_page.noSignup'
- defaultMessage='No sign-up methods configured, please contact your system administrator.'
- />
- </span>
- );
- }
-
- return (
- <div>
- {buttons}
- </div>
- );
- }
-}
-
-ChooseAuthPage.propTypes = {
- updatePage: React.PropTypes.func
-};
diff --git a/webapp/components/team_signup_with_email.jsx b/webapp/components/team_signup_with_email.jsx
deleted file mode 100644
index 90a6e9773..000000000
--- a/webapp/components/team_signup_with_email.jsx
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ReactDOM from 'react-dom';
-import * as Utils from 'utils/utils.jsx';
-import * as Client from 'utils/client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';
-import {browserHistory} from 'react-router';
-
-const holders = defineMessages({
- emailError: {
- id: 'email_signup.emailError',
- defaultMessage: 'Please enter a valid email address'
- },
- address: {
- id: 'email_signup.address',
- defaultMessage: 'Email Address'
- }
-});
-
-import React from 'react';
-
-class EmailSignUpPage extends React.Component {
- constructor() {
- super();
-
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.state = {};
- }
- handleSubmit(e) {
- e.preventDefault();
- const team = {};
- const state = {serverError: null};
- let isValid = true;
-
- team.email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
- if (!team.email || !Utils.isEmail(team.email)) {
- state.emailError = this.props.intl.formatMessage(holders.emailError);
- isValid = false;
- } else {
- state.emailError = null;
- }
-
- if (!isValid) {
- this.setState(state);
- return;
- }
-
- Client.signupTeam(team.email,
- (data) => {
- if (data.follow_link) {
- browserHistory.push(data.follow_link);
- } else {
- browserHistory.push(`/signup_team_confirm/?email=${encodeURIComponent(team.email)}`);
- }
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
- render() {
- let serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- let emailError = null;
- if (this.state.emailError) {
- emailError = <div className='form-group has-error'><label className='control-label'>{this.state.emailError}</label></div>;
- }
-
- return (
- <form
- role='form'
- onSubmit={this.handleSubmit}
- >
- <div className='form-group'>
- <input
- autoFocus={true}
- type='email'
- ref='email'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.address)}
- maxLength='128'
- spellCheck='false'
- />
- {emailError}
- </div>
- <div className='form-group'>
- <button
- className='btn btn-md btn-primary'
- type='submit'
- >
- <FormattedMessage
- id='email_signup.createTeam'
- defaultMessage='Create Team'
- />
- </button>
- {serverError}
- </div>
- </form>
- );
- }
-}
-
-EmailSignUpPage.defaultProps = {
-};
-EmailSignUpPage.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(EmailSignUpPage);
diff --git a/webapp/components/team_signup_with_ldap.jsx b/webapp/components/team_signup_with_ldap.jsx
deleted file mode 100644
index 9d812e8ee..000000000
--- a/webapp/components/team_signup_with_ldap.jsx
+++ /dev/null
@@ -1,231 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import $ from 'jquery';
-import ReactDOM from 'react-dom';
-import * as utils from 'utils/utils.jsx';
-import * as Client from 'utils/client.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';
-import {browserHistory} from 'react-router';
-
-const holders = defineMessages({
- team_error: {
- id: 'ldap_signup.team_error',
- defaultMessage: 'Please enter a team name'
- },
- length_error: {
- id: 'ldap_signup.length_error',
- defaultMessage: 'Name must be 3 or more characters up to a maximum of 15'
- },
- teamName: {
- id: 'ldap_signup.teamName',
- defaultMessage: 'Enter name of new team'
- },
- badTeam: {
- id: 'login_ldap.badTeam',
- defaultMessage: 'Bad team name'
- },
- idReq: {
- id: 'login_ldap.idlReq',
- defaultMessage: 'An LDAP ID is required'
- },
- pwdReq: {
- id: 'login_ldap.pwdReq',
- defaultMessage: 'An LDAP password is required'
- },
- username: {
- id: 'login_ldap.username',
- defaultMessage: 'LDAP Username'
- },
- pwd: {
- id: 'login_ldap.pwd',
- defaultMessage: 'LDAP Password'
- }
-});
-
-import React from 'react';
-
-class LdapSignUpPage extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.valueChange = this.valueChange.bind(this);
-
- this.state = {name: '', id: '', password: ''};
- }
-
- handleSubmit(e) {
- e.preventDefault();
- const {formatMessage} = this.props.intl;
- var teamSignup = {};
- teamSignup.user = {};
- teamSignup.team = {};
- var state = this.state;
- state.serverError = null;
-
- teamSignup.team.display_name = this.state.name;
-
- if (!teamSignup.team.display_name) {
- state.serverError = formatMessage(holders.team_error);
- this.setState(state);
- return;
- }
-
- if (teamSignup.team.display_name.length <= 2) {
- state.serverError = formatMessage(holders.length_error);
- this.setState(state);
- return;
- }
-
- const id = this.refs.id.value.trim();
- if (!id) {
- state.serverError = formatMessage(holders.idReq);
- this.setState(state);
- return;
- }
-
- const password = this.refs.password.value.trim();
- if (!password) {
- state.serverError = formatMessage(holders.pwdReq);
- this.setState(state);
- return;
- }
-
- state.serverError = '';
- this.setState(state);
-
- teamSignup.team.name = utils.cleanUpUrlable(teamSignup.team.display_name);
- teamSignup.team.type = 'O';
-
- teamSignup.user.username = ReactDOM.findDOMNode(this.refs.id).value.trim();
- teamSignup.user.password = ReactDOM.findDOMNode(this.refs.password).value.trim();
- teamSignup.user.allow_marketing = true;
- teamSignup.user.ldap = true;
- teamSignup.user.auth_service = 'ldap';
-
- $('#ldap-button').button('loading');
-
- Client.createTeamWithLdap(teamSignup,
- () => {
- Client.track('signup', 'signup_team_ldap_complete');
-
- Client.loginByLdap(teamSignup.team.name, id, password,
- () => {
- browserHistory.push('/' + teamSignup.team.name + '/channels/town-square');
- },
- (err) => {
- $('#ldap-button').button('reset');
- this.setState({serverError: err.message});
- }
- );
- },
- (err) => {
- $('#ldap-button').button('reset');
- this.setState({serverError: err.message});
- }
- );
- }
-
- valueChange() {
- this.setState({
- name: ReactDOM.findDOMNode(this.refs.teamname).value.trim(),
- id: ReactDOM.findDOMNode(this.refs.id).value.trim(),
- password: ReactDOM.findDOMNode(this.refs.password).value.trim()
- });
- }
-
- render() {
- const {formatMessage} = this.props.intl;
- var nameError = null;
- var nameDivClass = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameDivClass += ' has-error';
- }
-
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var disabled = false;
- if (this.state.name.length <= 2) {
- disabled = true;
- }
-
- if (this.state.id.length <= 1) {
- disabled = true;
- }
-
- if (this.state.password.length <= 1) {
- disabled = true;
- }
-
- return (
- <form
- role='form'
- onSubmit={this.handleSubmit}
- >
- <div className={nameDivClass}>
- <input
- autoFocus={true}
- type='text'
- ref='teamname'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.teamName)}
- maxLength='128'
- onChange={this.valueChange}
- spellCheck='false'
- />
- {nameError}
- </div>
- <div className={nameDivClass}>
- <input
- className='form-control'
- ref='id'
- placeholder={formatMessage(holders.username)}
- spellCheck='false'
- onChange={this.valueChange}
- />
- </div>
- <div className={nameDivClass}>
- <input
- type='password'
- className='form-control'
- ref='password'
- placeholder={formatMessage(holders.pwd)}
- spellCheck='false'
- onChange={this.valueChange}
- />
- </div>
- <div className='form-group'>
- <a
- className='btn btn-custom-login ldap btn-full'
- key='ldap'
- id='ldap-button'
- href='#'
- onClick={this.handleSubmit}
- disabled={disabled}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='ldap_signup.ldap'
- defaultMessage='Create team with LDAP Account'
- />
- </span>
- </a>
- {serverError}
- </div>
- </form>
- );
- }
-}
-
-LdapSignUpPage.propTypes = {
- intl: intlShape.isRequired
-};
-
-export default injectIntl(LdapSignUpPage);
diff --git a/webapp/components/team_signup_with_sso.jsx b/webapp/components/team_signup_with_sso.jsx
deleted file mode 100644
index 78396eea8..000000000
--- a/webapp/components/team_signup_with_sso.jsx
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import ReactDOM from 'react-dom';
-import * as utils from 'utils/utils.jsx';
-import * as client from 'utils/client.jsx';
-import Constants from 'utils/constants.jsx';
-
-import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';
-
-const holders = defineMessages({
- team_error: {
- id: 'sso_signup.team_error',
- defaultMessage: 'Please enter a team name'
- },
- length_error: {
- id: 'sso_signup.length_error',
- defaultMessage: 'Name must be 3 or more characters up to a maximum of 15'
- },
- teamName: {
- id: 'sso_signup.teamName',
- defaultMessage: 'Enter name of new team'
- }
-});
-
-import React from 'react';
-import {browserHistory} from 'react-router';
-
-class SSOSignUpPage extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
- this.nameChange = this.nameChange.bind(this);
-
- this.state = {name: ''};
- }
- handleSubmit(e) {
- e.preventDefault();
- const {formatMessage} = this.props.intl;
- var team = {};
- var state = this.state;
- state.nameError = null;
- state.serverError = null;
-
- team.display_name = this.state.name;
-
- if (!team.display_name) {
- state.nameError = formatMessage(holders.team_error);
- this.setState(state);
- return;
- }
-
- if (team.display_name.length <= 2) {
- state.nameError = formatMessage(holders.length_error);
- this.setState(state);
- return;
- }
-
- team.name = utils.cleanUpUrlable(team.display_name);
- team.type = 'O';
-
- client.createTeamWithSSO(team,
- this.props.service,
- (data) => {
- if (data.follow_link) {
- window.location.href = data.follow_link;
- } else {
- browserHistory.push('/' + team.name + '/channels/town-square');
- }
- },
- (err) => {
- state.serverError = err.message;
- this.setState(state);
- }
- );
- }
- nameChange() {
- this.setState({name: ReactDOM.findDOMNode(this.refs.teamname).value.trim()});
- }
- render() {
- var nameError = null;
- var nameDivClass = 'form-group';
- if (this.state.nameError) {
- nameError = <label className='control-label'>{this.state.nameError}</label>;
- nameDivClass += ' has-error';
- }
-
- var serverError = null;
- if (this.state.serverError) {
- serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
- }
-
- var disabled = false;
- if (this.state.name.length <= 2) {
- disabled = true;
- }
-
- var button = null;
-
- if (this.props.service === Constants.GITLAB_SERVICE) {
- button = (
- <a
- className='btn btn-custom-login gitlab btn-full'
- key='gitlab'
- href='#'
- onClick={this.handleSubmit}
- disabled={disabled}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='sso_signup.gitlab'
- defaultMessage='Create team with GitLab Account'
- />
- </span>
- </a>
- );
- } else if (this.props.service === Constants.GOOGLE_SERVICE) {
- button = (
- <a
- className='btn btn-custom-login google btn-full'
- key='google'
- href='#'
- onClick={this.handleSubmit}
- disabled={disabled}
- >
- <span className='icon'/>
- <span>
- <FormattedMessage
- id='sso_signup.google'
- defaultMessage='Create team with Google Apps Account'
- />
- </span>
- </a>
- );
- }
-
- return (
- <form
- role='form'
- onSubmit={this.handleSubmit}
- >
- <div className={nameDivClass}>
- <input
- autoFocus={true}
- type='text'
- ref='teamname'
- className='form-control'
- placeholder={this.props.intl.formatMessage(holders.teamName)}
- maxLength='128'
- onChange={this.nameChange}
- spellCheck='false'
- />
- {nameError}
- </div>
- <div className='form-group'>
- {button}
- {serverError}
- </div>
- </form>
- );
- }
-}
-
-SSOSignUpPage.defaultProps = {
- service: ''
-};
-SSOSignUpPage.propTypes = {
- intl: intlShape.isRequired,
- service: React.PropTypes.string
-};
-
-export default injectIntl(SSOSignUpPage);
diff --git a/webapp/components/toggle_modal_button.jsx b/webapp/components/toggle_modal_button.jsx
index de225493c..69bdbbda0 100644
--- a/webapp/components/toggle_modal_button.jsx
+++ b/webapp/components/toggle_modal_button.jsx
@@ -15,7 +15,8 @@ export default class ModalToggleButton extends React.Component {
};
}
- show() {
+ show(e) {
+ e.preventDefault();
this.setState({show: true});
}
@@ -29,10 +30,10 @@ export default class ModalToggleButton extends React.Component {
// allow callers to provide an onClick which will be called before the modal is shown
let clickHandler = this.show;
if (onClick) {
- clickHandler = () => {
+ clickHandler = (e) => {
onClick();
- this.show();
+ this.show(e);
};
}
diff --git a/webapp/components/user_list.jsx b/webapp/components/user_list.jsx
index 3652723be..626cb3cf5 100644
--- a/webapp/components/user_list.jsx
+++ b/webapp/components/user_list.jsx
@@ -13,10 +13,18 @@ export default class UserList extends React.Component {
let content;
if (users.length > 0) {
content = users.map((user) => {
+ var teamMember;
+ for (var index in this.props.teamMembers) {
+ if (this.props.teamMembers[index].user_id === user.id) {
+ teamMember = this.props.teamMembers[index];
+ }
+ }
+
return (
<UserListRow
key={user.id}
user={user}
+ teamMember={teamMember}
actions={this.props.actions}
actionProps={this.props.actionProps}
/>
@@ -48,12 +56,14 @@ export default class UserList extends React.Component {
UserList.defaultProps = {
users: [],
+ teamMembers: [],
actions: [],
actionProps: {}
};
UserList.propTypes = {
users: React.PropTypes.arrayOf(React.PropTypes.object),
+ teamMembers: React.PropTypes.arrayOf(React.PropTypes.object),
actions: React.PropTypes.arrayOf(React.PropTypes.func),
actionProps: React.PropTypes.object
};
diff --git a/webapp/components/user_list_row.jsx b/webapp/components/user_list_row.jsx
index f6fd91688..a7838ec32 100644
--- a/webapp/components/user_list_row.jsx
+++ b/webapp/components/user_list_row.jsx
@@ -4,9 +4,10 @@
import Constants from 'utils/constants.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
import * as Utils from 'utils/utils.jsx';
+import Client from 'utils/web_client.jsx';
import React from 'react';
-export default function UserListRow({user, actions, actionProps}) {
+export default function UserListRow({user, teamMember, actions, actionProps}) {
const nameFormat = PreferenceStore.get(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', '');
let name = user.username;
@@ -21,6 +22,7 @@ export default function UserListRow({user, actions, actionProps}) {
<Action
key={index.toString()}
user={user}
+ teamMember={teamMember}
{...actionProps}
/>
);
@@ -35,7 +37,7 @@ export default function UserListRow({user, actions, actionProps}) {
className='more-modal__image'
width='38'
height='38'
- src={`/api/v1/users/${user.id}/image?time=${user.update_at}`}
+ src={`${Client.getUsersRoute()}/${user.id}/image?time=${user.update_at}`}
/>
<div
className='more-modal__details'
@@ -57,12 +59,17 @@ export default function UserListRow({user, actions, actionProps}) {
}
UserListRow.defaultProps = {
+ teamMember: {
+ team_id: '',
+ roles: ''
+ },
actions: [],
actionProps: {}
};
UserListRow.propTypes = {
user: React.PropTypes.object.isRequired,
+ teamMember: React.PropTypes.object.isRequired,
actions: React.PropTypes.arrayOf(React.PropTypes.func),
actionProps: React.PropTypes.object
};
diff --git a/webapp/components/user_profile.jsx b/webapp/components/user_profile.jsx
index d83ab7454..04b4f99a4 100644
--- a/webapp/components/user_profile.jsx
+++ b/webapp/components/user_profile.jsx
@@ -2,6 +2,7 @@
// See License.txt for license information.
import * as Utils from 'utils/utils.jsx';
+import Client from 'utils/web_client.jsx';
import {FormattedMessage} from 'react-intl';
@@ -28,7 +29,7 @@ export default class UserProfile extends React.Component {
if (this.props.user) {
name = Utils.displayUsername(this.props.user.id);
email = this.props.user.email;
- profileImg = '/api/v1/users/' + this.props.user.id + '/image?time=' + this.props.user.update_at;
+ profileImg = Client.getUsersRoute() + '/' + this.props.user.id + '/image?time=' + this.props.user.update_at;
}
if (this.props.overwriteName) {
diff --git a/webapp/components/user_settings/import_theme_modal.jsx b/webapp/components/user_settings/import_theme_modal.jsx
index 2fc75ca13..32da296bf 100644
--- a/webapp/components/user_settings/import_theme_modal.jsx
+++ b/webapp/components/user_settings/import_theme_modal.jsx
@@ -5,7 +5,7 @@ import ReactDOM from 'react-dom';
import ModalStore from 'stores/modal_store.jsx';
import UserStore from 'stores/user_store.jsx';
import * as Utils from 'utils/utils.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import {Modal} from 'react-bootstrap';
import AppDispatcher from '../../dispatcher/app_dispatcher.jsx';
diff --git a/webapp/components/user_settings/manage_languages.jsx b/webapp/components/user_settings/manage_languages.jsx
index 094eaa127..bbf3a2e40 100644
--- a/webapp/components/user_settings/manage_languages.jsx
+++ b/webapp/components/user_settings/manage_languages.jsx
@@ -3,7 +3,7 @@
import SettingItemMax from '../setting_item_max.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as I18n from 'i18n/i18n.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
diff --git a/webapp/components/user_settings/user_settings_general.jsx b/webapp/components/user_settings/user_settings_general.jsx
index eddbc1efe..c6a05d1ee 100644
--- a/webapp/components/user_settings/user_settings_general.jsx
+++ b/webapp/components/user_settings/user_settings_general.jsx
@@ -9,7 +9,7 @@ import SettingPicture from '../setting_picture.jsx';
import UserStore from 'stores/user_store.jsx';
import ErrorStore from 'stores/error_store.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import Constants from 'utils/constants.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx';
@@ -225,11 +225,9 @@ class UserSettingsGeneralTab extends React.Component {
return;
}
- var formData = new FormData();
- formData.append('image', picture, picture.name);
this.setState({loadingPicture: true});
- Client.uploadProfileImage(formData,
+ Client.uploadProfileImage(picture,
() => {
this.submitActive = false;
AsyncClient.getMe();
@@ -781,7 +779,7 @@ class UserSettingsGeneralTab extends React.Component {
<SettingPicture
title={formatMessage(holders.profilePicture)}
submit={this.submitPicture}
- src={'/api/v1/users/' + user.id + '/image?time=' + user.last_picture_update}
+ src={Client.getUsersRoute() + '/' + user.id + '/image?time=' + user.last_picture_update}
server_error={serverError}
client_error={clientError}
updateSection={(e) => {
diff --git a/webapp/components/user_settings/user_settings_notifications.jsx b/webapp/components/user_settings/user_settings_notifications.jsx
index b119c42f9..fa84ce2d6 100644
--- a/webapp/components/user_settings/user_settings_notifications.jsx
+++ b/webapp/components/user_settings/user_settings_notifications.jsx
@@ -8,7 +8,7 @@ import SettingItemMax from '../setting_item_max.jsx';
import UserStore from 'stores/user_store.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx';
diff --git a/webapp/components/user_settings/user_settings_security.jsx b/webapp/components/user_settings/user_settings_security.jsx
index ff5a898a9..f28e34197 100644
--- a/webapp/components/user_settings/user_settings_security.jsx
+++ b/webapp/components/user_settings/user_settings_security.jsx
@@ -10,7 +10,7 @@ import ToggleModalButton from '../toggle_modal_button.jsx';
import TeamStore from 'stores/team_store.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
@@ -96,12 +96,10 @@ class SecurityTab extends React.Component {
return;
}
- var data = {};
- data.user_id = user.id;
- data.current_password = currentPassword;
- data.new_password = newPassword;
-
- Client.updatePassword(data,
+ Client.updatePassword(
+ user.id,
+ currentPassword,
+ newPassword,
() => {
this.props.updateSection('');
AsyncClient.getMe();
@@ -120,11 +118,9 @@ class SecurityTab extends React.Component {
);
}
activateMfa() {
- const data = {};
- data.activate = true;
- data.token = this.state.mfaToken;
-
- Client.updateMfa(data,
+ Client.updateMfa(
+ this.state.mfaToken,
+ true,
() => {
this.props.updateSection('');
AsyncClient.getMe();
@@ -224,7 +220,7 @@ class SecurityTab extends React.Component {
<div className='col-sm-7'>
<img
className='qr-code-img'
- src={'/api/v1/users/generate_mfa_qr?time=' + this.props.user.update_at}
+ src={Client.getUsersRoute() + '/generate_mfa_qr?time=' + this.props.user.update_at}
/>
</div>
<br/>
@@ -531,9 +527,9 @@ class SecurityTab extends React.Component {
if (global.window.mm_config.EnableSignUpWithEmail === 'true' && user.auth_service !== '') {
let link;
if (user.auth_service === Constants.LDAP_SERVICE) {
- link = '/' + teamName + '/claim/ldap_to_email?email=' + encodeURIComponent(user.email);
+ link = '/claim/ldap_to_email?email=' + encodeURIComponent(user.email);
} else {
- link = '/' + teamName + '/claim/oauth_to_email?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service;
+ link = '/claim/oauth_to_email?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service;
}
emailOption = (
@@ -558,7 +554,7 @@ class SecurityTab extends React.Component {
<div>
<Link
className='btn btn-primary'
- to={'/' + teamName + '/claim/email_to_oauth?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service + '&new_type=' + Constants.GITLAB_SERVICE}
+ to={'/claim/email_to_oauth?email=' + encodeURIComponent(user.email) + '&old_type=' + user.auth_service + '&new_type=' + Constants.GITLAB_SERVICE}
>
<FormattedMessage
id='user.settings.security.switchGitlab'
@@ -594,7 +590,7 @@ class SecurityTab extends React.Component {
<div>
<Link
className='btn btn-primary'
- to={'/' + teamName + '/claim/email_to_ldap?email=' + encodeURIComponent(user.email)}
+ to={'/claim/email_to_ldap?email=' + encodeURIComponent(user.email)}
>
<FormattedMessage
id='user.settings.security.switchLdap'
@@ -660,6 +656,13 @@ class SecurityTab extends React.Component {
defaultMessage='GitLab SSO'
/>
);
+ } else if (this.props.user.auth_service === Constants.LDAP_SERVICE) {
+ describe = (
+ <FormattedMessage
+ id='user.settings.security.ldap'
+ defaultMessage='LDAP'
+ />
+ );
}
return (
diff --git a/webapp/components/user_settings/user_settings_theme.jsx b/webapp/components/user_settings/user_settings_theme.jsx
index 14991037d..f19538f71 100644
--- a/webapp/components/user_settings/user_settings_theme.jsx
+++ b/webapp/components/user_settings/user_settings_theme.jsx
@@ -11,7 +11,7 @@ import SettingItemMax from '../setting_item_max.jsx';
import UserStore from 'stores/user_store.jsx';
import AppDispatcher from '../../dispatcher/app_dispatcher.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
diff --git a/webapp/components/view_image.jsx b/webapp/components/view_image.jsx
index 3d3107d92..bd4aeaa41 100644
--- a/webapp/components/view_image.jsx
+++ b/webapp/components/view_image.jsx
@@ -3,7 +3,7 @@
import $ from 'jquery';
import * as AsyncClient from 'utils/async_client.jsx';
-import * as Client from 'utils/client.jsx';
+import Client from 'utils/web_client.jsx';
import * as Utils from 'utils/utils.jsx';
import AudioVideoPreview from './audio_video_preview.jsx';
import Constants from 'utils/constants.jsx';
diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json
index da69aba74..6d4f4c287 100644
--- a/webapp/i18n/en.json
+++ b/webapp/i18n/en.json
@@ -271,6 +271,9 @@
"admin.ldap.lastnameAttrDesc": "The attribute in the LDAP server that will be used to populate the last name of users in Mattermost.",
"admin.ldap.lastnameAttrEx": "Ex \"sn\"",
"admin.ldap.lastnameAttrTitle": "Last Name Attribute:",
+ "admin.ldap.nicknameAttrDesc": "(Optional) The attribute in the LDAP server that will be used to populate the nickname of users in Mattermost.",
+ "admin.ldap.nicknameAttrEx": "Ex \"nickname\"",
+ "admin.ldap.nicknameAttrTitle": "Nickname Attribute:",
"admin.ldap.noLicense": "<h4 class=\"banner__heading\">Note:</h4><p>LDAP is an enterprise feature. Your current license does not support LDAP. Click <a href=\"http://mattermost.com\"target=\"_blank\">here</a> for information and pricing on enterprise licenses.</p>",
"admin.ldap.portDesc": "The port Mattermost will use to connect to the LDAP server. Default is 389.",
"admin.ldap.portEx": "Ex \"389\"",
@@ -526,10 +529,18 @@
"admin.team.uploading": "Uploading..",
"admin.team.userCreationDescription": "When false, the ability to create accounts is disabled. The create account button displays error when pressed.",
"admin.team.userCreationTitle": "Enable User Creation: ",
+ "admin.team.openServerDescription": "When true, anyone can signup for a user account on this server without the need to be invited.",
+ "admin.team.openServerTitle": "Enable Open Server: ",
"admin.team_analytics.activeUsers": "Active Users With Posts",
"admin.team_analytics.totalPosts": "Total Posts",
"admin.userList.title": "Users for {team}",
"admin.userList.title2": "Users for {team} ({count})",
+ "admin.user_item.resetMfa": "Remove MFA",
+ "admin.user_item.mfaYes": ", <strong>MFA</strong>: Yes",
+ "admin.user_item.mfaNo": ", <strong>MFA</strong>: No",
+ "admin.user_item.authServiceNotEmail": ", <strong>Sign-in Method:</strong> {service}",
+ "admin.user_item.authServiceEmail": ", <strong>Sign-in Method:</strong> Email",
+ "admin.user_item.emailTitle": "<strong>Email:</strong> {email}",
"admin.user_item.confirmDemoteDescription": "If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you'll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command.",
"admin.user_item.confirmDemoteRoleTitle": "Confirm demotion from System Admin role",
"admin.user_item.confirmDemotion": "Confirm Demotion",
@@ -721,7 +732,7 @@
"choose_auth_page.noSignup": "No sign-up methods configured, please contact your system administrator.",
"claim.account.noEmail": "No email specified",
"claim.email_to_ldap.enterLdapPwd": "Enter the ID and password for your LDAP account",
- "claim.email_to_ldap.enterPwd": "Enter the password for your {team} {site} email account",
+ "claim.email_to_ldap.enterPwd": "Enter the password for your {site} email account",
"claim.email_to_ldap.ldapId": "LDAP ID",
"claim.email_to_ldap.ldapIdError": "Please enter your LDAP ID.",
"claim.email_to_ldap.ldapPasswordError": "Please enter your LDAP password.",
@@ -732,7 +743,7 @@
"claim.email_to_ldap.ssoType": "Upon claiming your account, you will only be able to login with LDAP",
"claim.email_to_ldap.switchTo": "Switch account to LDAP",
"claim.email_to_ldap.title": "Switch Email/Password Account to LDAP",
- "claim.email_to_oauth.enterPwd": "Enter the password for your {team} {site} account",
+ "claim.email_to_oauth.enterPwd": "Enter the password for your {site} account",
"claim.email_to_oauth.pwd": "Password",
"claim.email_to_oauth.pwdError": "Please enter your password.",
"claim.email_to_oauth.ssoNote": "You must already have a valid {type} account",
@@ -741,7 +752,7 @@
"claim.email_to_oauth.title": "Switch Email/Password Account to {uiType}",
"claim.ldap_to_email.confirm": "Confirm Password",
"claim.ldap_to_email.email": "You will use the email {email} to login",
- "claim.ldap_to_email.enterLdapPwd": "Enter your LDAP password for your {team} {site} email account",
+ "claim.ldap_to_email.enterLdapPwd": "Enter your {ldapPassword} for your {site} email account",
"claim.ldap_to_email.enterPwd": "Enter a new password for your email account",
"claim.ldap_to_email.ldapPasswordError": "Please enter your LDAP password.",
"claim.ldap_to_email.ldapPwd": "LDAP Password",
@@ -758,7 +769,7 @@
"claim.oauth_to_email.pwdNotMatch": "Password do not match.",
"claim.oauth_to_email.switchTo": "Switch {type} to email and password",
"claim.oauth_to_email.title": "Switch {type} Account to Email",
- "claim.oauth_to_email_newPwd": "Enter a new password for your {team} {site} account",
+ "claim.oauth_to_email.enterNewPwd": "Enter a new password for your {site} account",
"confirm_modal.cancel": "Cancel",
"create_comment.addComment": "Add a comment...",
"create_comment.comment": "Add Comment",
@@ -841,8 +852,8 @@
"general_tab.includeDirDesc": "Including this team will display the team name from the Team Directory section of the Home Page, and provide a link to the sign-in page.",
"general_tab.includeDirTitle": "Include this team in the Team Directory",
"general_tab.no": "No",
- "general_tab.openInviteDesc": "When allowed, a link to account creation will be included on the sign-in page of this team and allow any visitor to sign-up.",
- "general_tab.openInviteTitle": "Allow anyone to sign-up from login page",
+ "general_tab.openInviteDesc": "When allowed, a link to this team will be including on the landing page allowing anyone with an account to join this team.",
+ "general_tab.openInviteTitle": "Allow anyone to join this team",
"general_tab.regenerate": "Re-Generate",
"general_tab.required": "This field is required",
"general_tab.teamName": "Team Name",
@@ -866,6 +877,7 @@
"installed_integrations.regenToken": "Regen Token",
"installed_integrations.search": "Search Integrations",
"installed_integrations.token": "Token: {token}",
+ "installed_integrations.url": "URL: {url}",
"installed_outgoing_webhooks.add": "Add Outgoing Webhook",
"installed_outgoing_webhooks.header": "Outgoing Webhooks",
"integrations.command.description": "Slash commands send events to external integrations",
@@ -913,6 +925,7 @@
"ldap_signup.team_error": "Please enter a team name",
"loading_screen.loading": "Loading",
"login.changed": " Sign-in method changed successfully",
+ "login.passwordChanged": " Password updated successfully",
"login.create": "Create one now",
"login.createTeam": "Create a new team",
"login.find": "Find your other teams",
@@ -1111,7 +1124,9 @@
"sidebar_right_menu.report": "Report a Problem",
"sidebar_right_menu.teamLink": "Get Team Invite Link",
"sidebar_right_menu.teamSettings": "Team Settings",
- "signup_team.choose": "Choose a Team",
+ "signup_team.no_teams": "You do not appear to be a member of any team. Please ask your administrator for an invite, join an open team if one exists or possibly create a new team.",
+ "signup_team.choose": "Teams you are a member of: ",
+ "signup_team.join_open": "Open teams you can join: ",
"signup_team.createTeam": "Or Create a Team",
"signup_team.disabled": "Team creation has been disabled. Please contact an administrator for access.",
"signup_team.noTeams": "There are no teams included in the Team Directory and team creation has been disabled.",
@@ -1119,6 +1134,8 @@
"signup_team_complete.completed": "You've already completed the signup process for this invitation or this invitation has expired.",
"signup_team_confirm.checkEmail": "Please check your email: <strong>{email}</strong><br />Your email contains a link to set up your team",
"signup_team_confirm.title": "Sign up Complete",
+ "signup_user_completed.no_open_server": "This server does not allow open signups. Please speak with your Administrator to receive an invitation.",
+ "signup_user_completed.invalid_invite": "The invite link was invalid. Please speak with your Administrator to receive an invitation.",
"signup_user_completed.choosePwd": "Choose your password",
"signup_user_completed.chooseUser": "Choose your username",
"signup_user_completed.create": "Create Account",
@@ -1181,61 +1198,23 @@
"team_settings_modal.generalTab": "General",
"team_settings_modal.importTab": "Import",
"team_settings_modal.title": "Team Settings",
- "team_signup_display_name.back": "Back to previous step",
- "team_signup_display_name.charLength": "Name must be 4 or more characters up to a maximum of 15",
- "team_signup_display_name.nameHelp": "Name your team in any language. Your team name shows in menus and headings.",
- "team_signup_display_name.next": "Next",
- "team_signup_display_name.required": "This field is required",
- "team_signup_display_name.teamName": "Team Name",
- "team_signup_email.address": "Email Address",
- "team_signup_email.different": "Please use a different email than the one used at signup",
- "team_signup_email.validEmail": "Please enter a valid email address",
- "team_signup_password.agreement": "By proceeding to create your account and use {siteName}, you agree to our <a href='/static/help/terms.html'>Terms of Service</a> and <a href='/static/help/privacy.html'>Privacy Policy</a>. If you do not agree, you cannot use {siteName}.",
- "team_signup_password.back": "Back to previous step",
- "team_signup_password.choosePwd": "Choose your password",
- "team_signup_password.creating": "Creating team...",
- "team_signup_password.email": "Email",
- "team_signup_password.finish": "Finish",
- "team_signup_password.hint": "Passwords must contain {min} to {max} characters. Your password will be strongest if it contains a mix of symbols, numbers, and upper and lowercase characters.",
- "team_signup_password.passwordError": "Please enter at least {chars} characters",
- "team_signup_password.selectPassword": "Select a password that you'll use to login with your email address:",
- "team_signup_password.yourPassword": "Your password",
- "team_signup_send_invites.addInvitation": "Add Invitation",
- "team_signup_send_invites.back": "Back to previous step",
- "team_signup_send_invites.disabled": "Email is currently disabled for your team, and emails cannot be sent. Contact your system administrator to enable email and email invitations.",
- "team_signup_send_invites.forNow": "for now.",
- "team_signup_send_invites.next": "Next",
- "team_signup_send_invites.prefer": "if you prefer, you can invite team members later<br /> and ",
- "team_signup_send_invites.skip": "skip this step ",
- "team_signup_send_invites.title": "Invite Team Members",
- "team_signup_url.back": "Back to previous step",
- "team_signup_url.charLength": "Name must be 4 or more characters up to a maximum of 15",
- "team_signup_url.hint": "<li>Short and memorable is best</li><li>Use lowercase letters, numbers and dashes</li><li>Must start with a letter and can't end in a dash</li>",
- "team_signup_url.next": "Next",
- "team_signup_url.regex": "Use only lower case letters, numbers and dashes. Must start with a letter and can't end in a dash.",
- "team_signup_url.required": "This field is required",
- "team_signup_url.taken": "URL is taken or contains a reserved word",
- "team_signup_url.teamUrl": "Team URL",
- "team_signup_url.unavailable": "This URL is unavailable. Please try another.",
- "team_signup_url.webAddress": "Choose the web address of your new team:",
- "team_signup_username.back": "Back to previous step",
- "team_signup_username.chooseUsername": "Choose your username",
- "team_signup_username.hint": "Usernames must begin with a letter and contain between {min} to {max} characters made up of lowercase letters, numbers, and the symbols '.', '-' and '_'",
- "team_signup_username.invalid": "Username must begin with a letter, and contain between {min} to {max} characters in total, which may be numbers, lowercase letters, or any of the symbols '.', '-', or '_'",
- "team_signup_username.memorable": "Select a memorable username that makes it easy for teammates to identify you:",
- "team_signup_username.next": "Next",
- "team_signup_username.reserved": "This username is reserved, please choose a new one.",
- "team_signup_username.username": "Your username",
- "team_signup_welcome.address": "Email Address",
- "team_signup_welcome.admin": "Your account will administer the new team site. <br />You can add other administrators later.",
- "team_signup_welcome.confirm": "Please confirm your email address:",
- "team_signup_welcome.different": "Use a different email",
- "team_signup_welcome.instead": "Use this instead",
- "team_signup_welcome.lets": "Let's set up your new team",
- "team_signup_welcome.storageError": "This service requires local storage to be enabled. Please enable it or exit private browsing.",
- "team_signup_welcome.validEmailError": "Please enter a valid email address",
- "team_signup_welcome.welcome": "Welcome to:",
- "team_signup_welcome.yes": "Yes, this address is correct",
+ "create_team.agreement": "By proceeding to create your account and use {siteName}, you agree to our <a href='/static/help/terms.html'>Terms of Service</a> and <a href='/static/help/privacy.html'>Privacy Policy</a>. If you do not agree, you cannot use {siteName}.",
+ "create_team.display_name.teamName": "Team Name",
+ "create_team.display_name.nameHelp": "Name your team in any language. Your team name shows in menus and headings.",
+ "create_team.display_name.next": "Next",
+ "create_team.display_name.back": "Back to previous step",
+ "create_team.display_name.required": "This field is required",
+ "create_team.display_name.charLength": "Name must be 4 or more characters up to a maximum of 15",
+ "create_team.team_url.required": "This field is required",
+ "create_team.team_url.regex": "Use only lower case letters, numbers and dashes. Must start with a letter and can't end in a dash.",
+ "create_team.team_url.charLength": "Name must be 4 or more characters up to a maximum of 15",
+ "create_team.team_url.taken": "URL is taken or contains a reserved word",
+ "create_team.team_url.unavailable": "This URL is unavailable. Please try another.",
+ "create_team.team_url.teamUrl": "Team URL",
+ "create_team.team_url.webAddress": "Choose the web address of your new team:",
+ "create_team.team_url.hint": "<li>Short and memorable is best</li><li>Use lowercase letters, numbers and dashes</li><li>Must start with a letter and can't end in a dash</li>",
+ "create_team.team_url.finish": "Finish",
+ "create_team.team_url.back": "Back to previous step",
"textbox.bold": "**bold**",
"textbox.edit": "Edit message",
"textbox.help": "Help",
@@ -1406,6 +1385,7 @@
"user.settings.security.currentPasswordError": "Please enter your current password",
"user.settings.security.emailPwd": "Email and Password",
"user.settings.security.gitlab": "GitLab SSO",
+ "user.settings.security.ldap": "LDAP",
"user.settings.security.lastUpdated": "Last updated {date} at {time}",
"user.settings.security.loginGitlab": "Login done through Gitlab",
"user.settings.security.loginLdap": "Login done through LDAP",
diff --git a/webapp/i18n/es.json b/webapp/i18n/es.json
index 23f1d5a45..7d429b629 100644
--- a/webapp/i18n/es.json
+++ b/webapp/i18n/es.json
@@ -721,7 +721,7 @@
"choose_auth_page.noSignup": "No hay métodos de inicio de sesión configurad, por favor contacte al administrador de sistemasos",
"claim.account.noEmail": "No se especifico un correo electrónico.",
"claim.email_to_ldap.enterLdapPwd": "Ingresa el ID y la contraseña de tu cuenta LDAP",
- "claim.email_to_ldap.enterPwd": "Ingresa la contraseña para tu cuenta de correo en {team} {site}",
+ "claim.email_to_ldap.enterPwd": "Ingresa la contraseña para tu cuenta de correo en {site}",
"claim.email_to_ldap.ldapId": "LDAP ID",
"claim.email_to_ldap.ldapIdError": "Por favor ingresa tu ID de LDAP.",
"claim.email_to_ldap.ldapPasswordError": "Por favor ingresa tu contraseña de LDAP.",
@@ -732,7 +732,7 @@
"claim.email_to_ldap.ssoType": "Al reclamar tu cuenta, sólo podrás iniciar sesión con LDAP",
"claim.email_to_ldap.switchTo": "Cambiar cuenta a LDAP",
"claim.email_to_ldap.title": "Cambiar Cuenta de Correo/Contraseña a LDAP",
- "claim.email_to_oauth.enterPwd": "Ingresa la contraseña para tu cuenta para {team} {site}",
+ "claim.email_to_oauth.enterPwd": "Ingresa la contraseña para tu cuenta para {site}",
"claim.email_to_oauth.pwd": "Contraseña",
"claim.email_to_oauth.pwdError": "Por favor introduce tu contraseña.",
"claim.email_to_oauth.ssoNote": "Debes tener una cuenta válida con {type}",
@@ -741,7 +741,6 @@
"claim.email_to_oauth.title": "Cambiar Cuenta de Correo/Contraseña a {uiType}",
"claim.ldap_to_email.confirm": "Confirmar Contraseña",
"claim.ldap_to_email.email": "Para iniciar sesión debes utilizar el correo electrónico {email}",
- "claim.ldap_to_email.enterLdapPwd": "Ingresa tu contraseña de LDAP para tu cuenta de correo en {team} {site}",
"claim.ldap_to_email.enterPwd": "Ingresa una nueva contraseña para tu cuenta de correo",
"claim.ldap_to_email.ldapPasswordError": "Por favor ingresa tu contraseña LDAP.",
"claim.ldap_to_email.ldapPwd": "Contraseña LDAP",
@@ -758,7 +757,6 @@
"claim.oauth_to_email.pwdNotMatch": "Las contraseñas no coinciden.",
"claim.oauth_to_email.switchTo": "Cambiar {type} a correo electrónico y contraseña",
"claim.oauth_to_email.title": "Cambiar la cuenta de {type} a Correo Electrónico",
- "claim.oauth_to_email_newPwd": "Ingresa una nueva contraseña para tu cuenta de {team} en {site}",
"confirm_modal.cancel": "Cancelar",
"create_comment.addComment": "Agregar un comentario...",
"create_comment.comment": "Agregar Comentario",
@@ -841,8 +839,6 @@
"general_tab.includeDirDesc": "Incluir este equipo mostrará el nombre del equipo en la sección de Directorio de Equipos en la página de inicio, y proveerá un enlace para la página de inicio de sesión.",
"general_tab.includeDirTitle": "Incluir este Equipo en el Directorio de Equipos",
"general_tab.no": "No",
- "general_tab.openInviteDesc": "Cuando está permitido, un enlace para la creación de cuentas será incluido en la página de registro de este equipo y permitirá a cualquier visitante registrarse.",
- "general_tab.openInviteTitle": "Permitir a cualquiera a inscribirse desde la página de inicio de sesión",
"general_tab.regenerate": "Regenerar",
"general_tab.required": "Este campo es obligatorio",
"general_tab.teamName": "Nombre del Equipo",
@@ -1111,7 +1107,6 @@
"sidebar_right_menu.report": "Reporta un Problema",
"sidebar_right_menu.teamLink": "Enlace Invitación al Equipo",
"sidebar_right_menu.teamSettings": "Configurar Equipo",
- "signup_team.choose": "Selecciona un Equipo",
"signup_team.createTeam": "O Crea un Equipo",
"signup_team.disabled": "La creación de Equipos ha sido deshabilitada.",
"signup_team.noTeams": "No hay equipos en el Directorio de Equipos y la creación de equipos ha sido deshabilitada.",
@@ -1181,61 +1176,6 @@
"team_settings_modal.generalTab": "General",
"team_settings_modal.importTab": "Importar",
"team_settings_modal.title": "Configuración del Equipo",
- "team_signup_display_name.back": "Volver al paso previo",
- "team_signup_display_name.charLength": "El nombre debe tener 4 o más caracteres hasta un máximo de 15",
- "team_signup_display_name.nameHelp": "Nombre tu equipo en cualquier idioma. El nombre de tu equipo aparecerá en menús y en inicios",
- "team_signup_display_name.next": "Siguiente",
- "team_signup_display_name.required": "Este campo es obligatorio",
- "team_signup_display_name.teamName": "Nombre del Equipo",
- "team_signup_email.address": "Dirección de correo electrónico",
- "team_signup_email.different": "Please use a different email than the one used at signup",
- "team_signup_email.validEmail": "Por favor ingresa una dirección de correo electrónico válida",
- "team_signup_password.agreement": "Procediendo a crear tu cuenta y el uso de {siteName}, indicas que estás de acuerdo con nuestros <a href='/static/help/terms.html'>Términos de Servicio</a> y <a href='/static/help/privacy.html'>Políticas de Privacidad</a>. Si no estás de acuerdo, no debes utilizar {siteName}.",
- "team_signup_password.back": "Volver al paso previo",
- "team_signup_password.choosePwd": "Escoge tu contraseña",
- "team_signup_password.creating": "Creando equipo...",
- "team_signup_password.email": "Correo electrónico",
- "team_signup_password.finish": "Finalizar",
- "team_signup_password.hint": "Las contraseñas deben contener de {min} a {max} caracteres. Su contraseña será más fuerte si contiene una mezcla de símbolos, números y caracteres en mayúsculas y minúsculas.",
- "team_signup_password.passwordError": "Por favor ingrese al menos {chars} caracteres",
- "team_signup_password.selectPassword": "Selecciona la contraseña que estás usando con tu dirección de correos:",
- "team_signup_password.yourPassword": "Tu contraseña",
- "team_signup_send_invites.addInvitation": "Agrega una Invitación",
- "team_signup_send_invites.back": "Volver al paso previo",
- "team_signup_send_invites.disabled": "Este correo electrónico está actualmente deshabilitado para tu equipo, y los correos no podrán ser enviados. Contacta a tu administrador de sistemas",
- "team_signup_send_invites.forNow": "por ahora.",
- "team_signup_send_invites.next": "Siguiente",
- "team_signup_send_invites.prefer": "Si prefieres, puedes invitar a miembros de equipo más tarde <br /> y ",
- "team_signup_send_invites.skip": "saltarte este paso ",
- "team_signup_send_invites.title": "Invita Miembros al Equipo",
- "team_signup_url.back": "Volver al paso previo",
- "team_signup_url.charLength": "El nombre debe tener 4 o más caracteres hasta un máximo de 15",
- "team_signup_url.hint": "<li>Corto y memorizable es mejor</li><li>Use letras en minúsculas, números y guiones</li><li>Debe empezar con una letra y no puede finalizar con un guión</li>",
- "team_signup_url.next": "Siguiente",
- "team_signup_url.regex": "Sólo utiliza letras en minúsculas, numeros y guiones. Debe comenzar con una letra y no puede terminar en un guión.",
- "team_signup_url.required": "Este campo es obligatorio",
- "team_signup_url.taken": "Este URL ya fue asignado o contiene una palabra reservada",
- "team_signup_url.teamUrl": "URL de Equipo",
- "team_signup_url.unavailable": "Este URL no está disponible. Por favor intenta con otro.",
- "team_signup_url.webAddress": "Escoge la dirección web de tu nuevo equipo:",
- "team_signup_username.back": "Volver al paso previo",
- "team_signup_username.chooseUsername": "Escoge un nombre de usuario",
- "team_signup_username.hint": "El nombre de usuario debe empezar con una letra, y contener entre {min} a {max} caracteres en minúscula con números, letras, y los símbolos '.', '-' y '_'.",
- "team_signup_username.invalid": "El nombre de usuario debe comenzar con una letra, y tener entre {min} y {max} de caracteres en total, los cuales pueden ser numeros, letras en minúsculas, o cualquiera de los simbolos '.', '-', o '_'",
- "team_signup_username.memorable": "Selecciona un nombre de usuario sencillo de recordar y que sea fácil para a tus compañeros de equipo identificarte:",
- "team_signup_username.next": "Siguiente",
- "team_signup_username.reserved": "Este nombre de usuario está reservado. Por favor escoge otro.",
- "team_signup_username.username": "Tu nombre de usuario",
- "team_signup_welcome.address": "Dirección de correo",
- "team_signup_welcome.admin": "Tu cuenta administrará un nuevo sitio del equipo. <br />Puedes agregar otros administradores más adelante.",
- "team_signup_welcome.confirm": "Por favor confirma tu dirección de correos:",
- "team_signup_welcome.different": "Usa un correo diferente",
- "team_signup_welcome.instead": "Usa este en vez de",
- "team_signup_welcome.lets": "permítenos setear tu nuevo equipo",
- "team_signup_welcome.storageError": "Este servicio requiere de almacenamiento local para ser habilitado. Por favor habilítalo o sale de la navegación privad.",
- "team_signup_welcome.validEmailError": "Por favor ingresa una dirección de correo electrónico válida",
- "team_signup_welcome.welcome": "Bienvenido a:",
- "team_signup_welcome.yes": "Sí, esta dirección es correcta",
"textbox.bold": "**negritas**",
"textbox.edit": "Editar mensaje",
"textbox.help": "Ayuda",
diff --git a/webapp/i18n/fr.json b/webapp/i18n/fr.json
index 44bf36ef6..1e2ea0e9b 100644
--- a/webapp/i18n/fr.json
+++ b/webapp/i18n/fr.json
@@ -647,7 +647,7 @@
"choose_auth_page.noSignup": "Aucune méthode configurée pour s'inscrive, veuillez contacter votre administrateur système.",
"claim.account.noEmail": "Aucune adresse électronique indiquée",
"claim.email_to_ldap.enterLdapPwd": "Saisissez l'identifiant et le mode de passe de votre compte LDAP",
- "claim.email_to_ldap.enterPwd": "Saisissez le mot de passe pour votre compte {team} {site}",
+ "claim.email_to_ldap.enterPwd": "Saisissez le mot de passe pour votre compte {site}",
"claim.email_to_ldap.ldapId": "Identifiant LDAP",
"claim.email_to_ldap.ldapIdError": "Veuillez saisir votre identifiant LDAP.",
"claim.email_to_ldap.ldapPasswordError": "Veuillez saisir votre mot de passe LDAP.",
@@ -658,7 +658,7 @@
"claim.email_to_ldap.ssoType": "Une fois votre compte configuré, vous ne pourrez vous connectez qu'avec LDAP",
"claim.email_to_ldap.switchTo": "Basculer le compte vers LDAP",
"claim.email_to_ldap.title": "Transférer le login par courriel/mot de passe en LDAP",
- "claim.email_to_oauth.enterPwd": "Saisissez le mot de passe pour votre compte {team} {site}",
+ "claim.email_to_oauth.enterPwd": "Saisissez le mot de passe pour votre compte {site}",
"claim.email_to_oauth.pwd": "Mot de passe",
"claim.email_to_oauth.pwdError": "Veuillez saisir votre mot de passe.",
"claim.email_to_oauth.ssoNote": "Vous devez déjà avoir un compte {type} valide",
@@ -667,7 +667,6 @@
"claim.email_to_oauth.title": "Changer l'adresse électronique/mot de passe pour {uiType}",
"claim.ldap_to_email.confirm": "Confirmer le mot de passe",
"claim.ldap_to_email.email": "Vous devrez utiliser l'adresse électronique {email} pour vous connecter.",
- "claim.ldap_to_email.enterLdapPwd": "Saisissez votre mot de passe LDAP pour votre compte {team} {site}",
"claim.ldap_to_email.enterPwd": "Saisissez un nouveau mot de passe pour votre compte",
"claim.ldap_to_email.ldapPasswordError": "Veuillez saisir votre mot de passe LDAP.",
"claim.ldap_to_email.ldapPwd": "Mot de passe LDAP",
@@ -680,7 +679,7 @@
"claim.oauth_to_email.confirm": "Confirmez le mot de passe",
"claim.oauth_to_email.description": "Une fois votre compte modifié, vous ne pourrez plus vous connecter qu'à l'aide de votre adresse électronique et votre mot de passe.",
"claim.oauth_to_email.enterPwd": "Veuillez saisir un mot de passe.",
- "claim.oauth_to_email.newPwd": "Saisissez un nouveau mot de passe pour votre compte {team} {site}",
+ "claim.oauth_to_email.newPwd": "Saisissez un nouveau mot de passe pour votre compte {site}",
"claim.oauth_to_email.pwdNotMatch": "Le mot de passe ne correspond pas.",
"claim.oauth_to_email.switchTo": "Basculer de {type} vers adresse électronique et mot de passe",
"claim.oauth_to_email.title": "Basculer du compte {type} vers l'adresse électronique",
@@ -765,8 +764,6 @@
"general_tab.includeDirDesc": "Inclure cette équipe affichera le nom de l'équipe dans l'annuaire sur la page d'accueil, ainsi qu'un lien pour rejoindre cette équipe.",
"general_tab.includeDirTitle": "Afficher cette équipe dans l'annuaire",
"general_tab.no": "Non",
- "general_tab.openInviteDesc": "Si activé, un lien pour créer un compte est affiché sur la page de connexion de l'équipe, et permet à n'importe qui de rejoindre l'équipe.",
- "general_tab.openInviteTitle": "Permettre à tout le monde de s'inscrire",
"general_tab.regenerate": "Générer de nouveau",
"general_tab.required": "Ce champ est obligatoire",
"general_tab.teamName": "Nom de l'équipe",
@@ -1012,7 +1009,6 @@
"sidebar_right_menu.report": "Signaler un problème",
"sidebar_right_menu.teamLink": "Obtenir un lien d'invitation d'équipe",
"sidebar_right_menu.teamSettings": "Configuration de l'équipe",
- "signup_team.choose": "Choisir une équipe",
"signup_team.createTeam": "Ou créez une équipe",
"signup_team.disabled": "Aucune méthode de création d'utilisateur n'est disponible. Veuillez contacter votre administrateur système pour obtenir un accès.",
"signup_team.noTeams": "Il n'y a aucune équipe dans l'annuaire, et la création d'équipe n'est pas autorisée.",
@@ -1081,61 +1077,6 @@
"team_settings_modal.generalTab": "Général",
"team_settings_modal.importTab": "Importer",
"team_settings_modal.title": "Configuration de l'équipe",
- "team_signup_display_name.back": "Revenir à l'étape précédente",
- "team_signup_display_name.charLength": "Le nom doit être composé de 4 à 15 caractères",
- "team_signup_display_name.nameHelp": "Donnez un nom à votre équipe. Le nom de votre équipe apparait dans les menus et les en-têtes.",
- "team_signup_display_name.next": "Suivant",
- "team_signup_display_name.required": "Ce champ est obligatoire",
- "team_signup_display_name.teamName": "Nom de l'équipe",
- "team_signup_email.address": "Adresse électronique",
- "team_signup_email.different": "Veuillez utiliser une autre adresse électronique que celle utilisée pour votre inscription",
- "team_signup_email.validEmail": "Veuillez saisir une adresse électronique valide",
- "team_signup_password.agreement": "En créant ce compte et en utilisant {siteName}, vous consentez à nos <a href='/static/help/terms.html'>Conditions d'utilisation</a> et <a href='/static/help/privacy.html'>Politique de Confidentialité</a>. Si vous n'y consentez pas, vous ne pouvez pas utiliser {siteName}.",
- "team_signup_password.back": "Retour à l'étape précédente",
- "team_signup_password.choosePwd": "Choisissez votre mot de passe",
- "team_signup_password.creating": "Création de l'équipe...",
- "team_signup_password.email": "Adresse électronique",
- "team_signup_password.finish": "Terminé",
- "team_signup_password.hint": "Les mots de passe doivent contenir entre {min} et {max} caractère. Votre mot de passe sera plus sécurisé s'il contient un mélange de symboles, de chiffres, et de lettres majuscules et minuscules",
- "team_signup_password.passwordError": "Veuillez saisir au moins {chars} caractères",
- "team_signup_password.selectPassword": "Choisissez le mot de passe que vous utiliserez pour vous connecter avec votre adresse électronique :",
- "team_signup_password.yourPassword": "Votre mot de passe",
- "team_signup_send_invites.addInvitation": "Ajouter une invitation",
- "team_signup_send_invites.back": "Retour à l'étape précédente",
- "team_signup_send_invites.disabled": "Les courriels sont désactivés pour votre équipe et ne peuvent pas être envoyés. Contactez votre administrateur système pour activer les courriels et les invitations par courriel.",
- "team_signup_send_invites.forNow": "pour l'instant.",
- "team_signup_send_invites.next": "Suivant",
- "team_signup_send_invites.prefer": "Vous pouvez aussi inviter des membres plus tard<br /> et",
- "team_signup_send_invites.skip": "passer cette étape",
- "team_signup_send_invites.title": "Inviter des membres",
- "team_signup_url.back": "Retour à l'étape précédente",
- "team_signup_url.charLength": "Le nom doit contenir entre 4 et 15 caractères",
- "team_signup_url.hint": "<li>Court et facile à retenir, c'est mieux !</li><li>Utilisez des lettres minuscules, des chiffres et des tirets</li><li>Doit commencer par une lettre et ne peut pas finir par un tiret.</li>",
- "team_signup_url.next": "Suivant",
- "team_signup_url.regex": "Utilisez seulement des lettres minuscules, des chiffres et des tirets. Doit commencer par une lettre et ne doit pas finir par un tiret.",
- "team_signup_url.required": "Champ obligatoire",
- "team_signup_url.taken": "Cette URL est indisponible ou contient un mot réservé",
- "team_signup_url.teamUrl": "URL de l'équipe",
- "team_signup_url.unavailable": "Cette URL est indisponible. Veuillez essayer une autre URL.",
- "team_signup_url.webAddress": "Choisissez l'adresse internet de votre nouvelle équipe :",
- "team_signup_username.back": "Retour à l'étape précédente",
- "team_signup_username.chooseUsername": "Choisissez votre nom d'utilisateur",
- "team_signup_username.hint": "Les noms d'utilisateurs doivent commencer par une lettre et contenir entre {min} et {max} caractères composés de lettres minuscules, de chiffres et des symboles '.', '-' et '_'.",
- "team_signup_username.invalid": "Les nomes d'utilisateur doivent commencer par une lettre et contenir entre {min} et {max} caractères, qui peuvent être des chiffres, des lettres minuscules ou les symboles '.', '-' ou '_'.",
- "team_signup_username.memorable": "Choisissez un nom d'utilisateur facile à retenir qui permettra aux autres membres de l'équipe de vous identifier facilement :",
- "team_signup_username.next": "Suivant",
- "team_signup_username.reserved": "Ce nom d'utilisateur est réservé, veuilles en choisir un autre.",
- "team_signup_username.username": "Votre nom d'utilisateur",
- "team_signup_welcome.address": "Adresse électronique",
- "team_signup_welcome.admin": "Votre compte sera administrateur de votre nouvelle équipe. <br />Vous pourrez ajouter d'autres administrateurs par la suite.",
- "team_signup_welcome.confirm": "Veuillez confirmer votre adresse électronique :",
- "team_signup_welcome.different": "Utiliser une autre adresse électronique",
- "team_signup_welcome.instead": "Utiliser plutôt ceci",
- "team_signup_welcome.lets": "Configurons ensemble votre nouvelle équipe",
- "team_signup_welcome.storageError": "Ce service nécessite l'utilisation des cookies. Veuillez permettre le stockage des cookies ou quitter la navigation privée.",
- "team_signup_welcome.validEmailError": "Veuillez entrer une adresse électronique valide",
- "team_signup_welcome.welcome": "Bienvenue sur :",
- "team_signup_welcome.yes": "Oui, cette adresse est correcte",
"textbox.bold": "**gras**",
"textbox.edit": "Modifier le message",
"textbox.help": "Aide",
diff --git a/webapp/i18n/ja.json b/webapp/i18n/ja.json
index ea951235e..eab97673e 100644
--- a/webapp/i18n/ja.json
+++ b/webapp/i18n/ja.json
@@ -1181,61 +1181,6 @@
"team_settings_modal.generalTab": "全般",
"team_settings_modal.importTab": "インポート",
"team_settings_modal.title": "チームの設定",
- "team_signup_display_name.back": "前のステップに戻る",
- "team_signup_display_name.charLength": "名前は4文字以上の15文字以下にしてください",
- "team_signup_display_name.nameHelp": "チーム名はどんな言語でも使うことができます。チーム名はメニューと画面上部に表示されます。",
- "team_signup_display_name.next": "次へ",
- "team_signup_display_name.required": "この項目は必須です",
- "team_signup_display_name.teamName": "チーム名",
- "team_signup_email.address": "電子メールアドレス",
- "team_signup_email.different": "利用登録で使用した電子メールアドレスとは別の電子メールアドレスを使ってください",
- "team_signup_email.validEmail": "有効な電子メールアドレスを入力してください",
- "team_signup_password.agreement": "{siteName}にアカウントを作成し利用する前に<a href='/static/help/terms.html'>使用条件</a>と<a href='/static/help/privacy.html'>プライバシーポリシー</a>に同意してください。同意できない場合は{siteName}は使用できません。",
- "team_signup_password.back": "前のステップに戻る",
- "team_signup_password.choosePwd": "パスワードを入力してください",
- "team_signup_password.creating": "チームを作成しています…",
- "team_signup_password.email": "電子メールアドレス",
- "team_signup_password.finish": "完了する",
- "team_signup_password.hint": "パスワードは{min}から{max}文字にしてください。パスワードを強固にするには、記号、数字、大文字と小文字の英字が混在するようにしてください。",
- "team_signup_password.passwordError": "少なくとも{chars}文字を入力してください。",
- "team_signup_password.selectPassword": "電子メールアドレスでログインする場合のパスワードを入力してください:",
- "team_signup_password.yourPassword": "あなたのパスワード",
- "team_signup_send_invites.addInvitation": "招待する",
- "team_signup_send_invites.back": "前のステップに戻る",
- "team_signup_send_invites.disabled": "あなたのチームでは電子メールは有効になっていません。電子メールによる招待状は送信できません。システム管理者に電子メールと電子メールによる招待を有効にするように連絡してください。",
- "team_signup_send_invites.forNow": "いますぐ。",
- "team_signup_send_invites.next": "次へ",
- "team_signup_send_invites.prefer": "後ほどチームメンバーを招待することもできます<br />また ",
- "team_signup_send_invites.skip": "このステップはスキップできます ",
- "team_signup_send_invites.title": "チームメンバーを招待する",
- "team_signup_url.back": "前のステップに戻る",
- "team_signup_url.charLength": "名前は4文字以上の15文字以下にしてください",
- "team_signup_url.hint": "<li>短く覚えやすいものがベストです</li><li>英小文字、数字、ダッシュを使ってください</li><li>英小文字で始めてください。ダッシュで終わることはできません</li>",
- "team_signup_url.next": "次へ",
- "team_signup_url.regex": "英小文字、数字、ダッシュのみ使用できます。英小文字で始めてください。ダッシュで終わることはできません。",
- "team_signup_url.required": "この項目は必須です",
- "team_signup_url.taken": "URLが取得済みか、予約された単語を含んでいます",
- "team_signup_url.teamUrl": "チームURL",
- "team_signup_url.unavailable": "このURLは使用できません。他のものを試してください。",
- "team_signup_url.webAddress": "あなたの新しいチームのウェブアドレスを選択してください。",
- "team_signup_username.back": "前のステップに戻る",
- "team_signup_username.chooseUsername": "ユーザー名を入力してください",
- "team_signup_username.hint": "ユーザー名は英小文字で始めてください。また{min}から{max} 文字の英数字と'.'、'-'、'_'の記号だけで構成してください。",
- "team_signup_username.invalid": "ユーザー名は英小文字で始めてください。また{min}から{max} 文字の英数字と'.'、'-'、'_'の記号だけで構成してください。",
- "team_signup_username.memorable": "チームメイトが認識しやすいように覚えやすいユーザー名を選択してください:",
- "team_signup_username.next": "次へ",
- "team_signup_username.reserved": "このユーザー名は予約されています。他のユーザー名を使ってください。",
- "team_signup_username.username": "あなたのユーザー名",
- "team_signup_welcome.address": "電子メールアドレス",
- "team_signup_welcome.admin": "あなたは新しいチームサイトの管理者になります。<br />後ほど他の人を管理者として追加することができます。",
- "team_signup_welcome.confirm": "あなたの電子メールアドレスを確認してください:",
- "team_signup_welcome.different": "違う電子メールアドレスを使う",
- "team_signup_welcome.instead": "これを代わりに使用する",
- "team_signup_welcome.lets": "新しいチームを作りましょう",
- "team_signup_welcome.storageError": "このサービスを使用するにはローカルストレージを有効にしてください。有効にするかプライベートブラウジングを止めてください。",
- "team_signup_welcome.validEmailError": "有効な電子メールアドレスを入力してください",
- "team_signup_welcome.welcome": "ようこそ:",
- "team_signup_welcome.yes": "この電子メールアドレスは正しいです",
"textbox.bold": "**太字**",
"textbox.edit": "メッセージを編集する",
"textbox.help": "ヘルプ",
diff --git a/webapp/i18n/pt.json b/webapp/i18n/pt.json
index 97fea8396..0fcb958c2 100644
--- a/webapp/i18n/pt.json
+++ b/webapp/i18n/pt.json
@@ -721,7 +721,7 @@
"choose_auth_page.noSignup": "Nenhum método de inscrição configurado, por favor contate seu administrador do sistema.",
"claim.account.noEmail": "Nenhum email específicado",
"claim.email_to_ldap.enterLdapPwd": "Entre o ID e a senha para sua conta LDAP",
- "claim.email_to_ldap.enterPwd": "Entre a senha para o sua conta com email {team} {site}",
+ "claim.email_to_ldap.enterPwd": "Entre a senha para o sua conta com email {site}",
"claim.email_to_ldap.ldapId": "LDAP ID",
"claim.email_to_ldap.ldapIdError": "Por favor digite seu ID LDAP.",
"claim.email_to_ldap.ldapPasswordError": "Por favor digite a sua senha LDAP.",
@@ -732,7 +732,7 @@
"claim.email_to_ldap.ssoType": "Ao retirar a sua conta, você só vai ser capaz de logar com LDAP",
"claim.email_to_ldap.switchTo": "Trocar a conta para LDAP",
"claim.email_to_ldap.title": "Trocar E-mail/Senha da Conta para LDAP",
- "claim.email_to_oauth.enterPwd": "Entre a senha para o sua conta {team} {site}",
+ "claim.email_to_oauth.enterPwd": "Entre a senha para o sua conta {site}",
"claim.email_to_oauth.pwd": "Senha",
"claim.email_to_oauth.pwdError": "Por favor digite a sua senha.",
"claim.email_to_oauth.ssoNote": "Você precisa já ter uma conta {type} válida",
@@ -741,7 +741,6 @@
"claim.email_to_oauth.title": "Trocar E-mail/Senha da Conta para {uiType}",
"claim.ldap_to_email.confirm": "Confirmar senha",
"claim.ldap_to_email.email": "Você vai usar o email {email} para logar",
- "claim.ldap_to_email.enterLdapPwd": "Entre a sua senha LDAP para o sua conta {team} {site}",
"claim.ldap_to_email.enterPwd": "Entre a nova senha para o sua conta com email.",
"claim.ldap_to_email.ldapPasswordError": "Por favor digite a sua senha LDAP.",
"claim.ldap_to_email.ldapPwd": "Senha LDAP",
@@ -758,7 +757,6 @@
"claim.oauth_to_email.pwdNotMatch": "As senha não correspondem.",
"claim.oauth_to_email.switchTo": "Trocar {type} para email e senha",
"claim.oauth_to_email.title": "Trocar Conta {type} para E-mail",
- "claim.oauth_to_email_newPwd": "Entre a nova senha para o sua conta {team} {site}",
"confirm_modal.cancel": "Cancelar",
"create_comment.addComment": "Adicionar um comentário...",
"create_comment.comment": "Adicionar Comentário",
@@ -841,8 +839,6 @@
"general_tab.includeDirDesc": "Incluindo esta equipe irá exibir o nome da equipe da seção Diretório Equipe da página inicial, e fornecer um link para a página de login.",
"general_tab.includeDirTitle": "Incluir esta equipe no Diretório de Equipe",
"general_tab.no": "Não",
- "general_tab.openInviteDesc": "Quando permitido, um link para a criação da conta será incluído na página de login da equipe e permitir que qualquer visitante inscreva-se.",
- "general_tab.openInviteTitle": "Permitir que qualquer pessoa se inscreva a partir da página de login",
"general_tab.regenerate": "Re-Gerar",
"general_tab.required": "Este campo é obrigatório",
"general_tab.teamName": "Nome da Equipe",
@@ -1111,7 +1107,6 @@
"sidebar_right_menu.report": "Relatar um Problema",
"sidebar_right_menu.teamLink": "Obter Link para Convite de Equipe",
"sidebar_right_menu.teamSettings": "Configurações da Equipe",
- "signup_team.choose": "Escolha uma Equipe",
"signup_team.createTeam": "Ou Criar uma Equipe",
"signup_team.disabled": "A criação de equipe foi desativada. Por favor, entre em contato com um administrador para o acesso.",
"signup_team.noTeams": "Não existe equipes incluidas no Diretório de Equipe e a criação de equipes foi desativada.",
@@ -1181,61 +1176,6 @@
"team_settings_modal.generalTab": "Geral",
"team_settings_modal.importTab": "Importar",
"team_settings_modal.title": "Configurações da Equipe",
- "team_signup_display_name.back": "Voltar para o passo anterior",
- "team_signup_display_name.charLength": "O nome deve ser de 4 ou mais caracteres até um máximo de 15",
- "team_signup_display_name.nameHelp": "Nome da sua equipe em qualquer idioma. Seu nome de equipe é mostrado em menus e títulos.",
- "team_signup_display_name.next": "Próximo",
- "team_signup_display_name.required": "Este campo é obrigatório",
- "team_signup_display_name.teamName": "Nome Da Equipe",
- "team_signup_email.address": "Endereço de E-mail",
- "team_signup_email.different": "Por favor, use um e-mail diferente do que o usado na inscrição",
- "team_signup_email.validEmail": "Por favor entre um endereço de e-mail válido",
- "team_signup_password.agreement": "Ao prosseguir para criar sua conta e usar {siteName}, você concorda com nosso <a href='/static/help/terms.html'>Termo de Serviço</a> e <a href='/static/help/privacy.html'>Politica de Privacidade</a>. Se você não concorda, você não pode usar {siteName}.",
- "team_signup_password.back": "Voltar para o passo anterior",
- "team_signup_password.choosePwd": "Escolha sua senha",
- "team_signup_password.creating": "Criando um equipe...",
- "team_signup_password.email": "E-mail",
- "team_signup_password.finish": "Terminar",
- "team_signup_password.hint": "Senhas precisam conter {min} a {max} caracteres. Sua senha será segura se conter uma mistura de símbolos, números, e caracteres maiúsculos e minúsculos.",
- "team_signup_password.passwordError": "Por favor, insira pelo menos {chars} caracteres",
- "team_signup_password.selectPassword": "Selecione uma senha que você irá usar no login com seu endereço de email:",
- "team_signup_password.yourPassword": "Sua senha",
- "team_signup_send_invites.addInvitation": "Adicionar Convite",
- "team_signup_send_invites.back": "Voltar para o passo anterior",
- "team_signup_send_invites.disabled": "Email está desativado para a sua equipe, e emails e não podem ser enviados. Contate o seu administrador do sistema para ativar e-mail e convites por e-mail.",
- "team_signup_send_invites.forNow": "agora.",
- "team_signup_send_invites.next": "Próximo",
- "team_signup_send_invites.prefer": "se você preferir, você pode convidar membros da equipe depois<br /> e ",
- "team_signup_send_invites.skip": "pular este passo ",
- "team_signup_send_invites.title": "Convidar Membros da Equipe",
- "team_signup_url.back": "Voltar para o passo anterior",
- "team_signup_url.charLength": "O nome deve ser de 4 ou mais caracteres até um máximo de 15",
- "team_signup_url.hint": "<li>Curto e memorizável é o melhor</li><li>Use letras minúsculas, números e traços</li><li>Deve começar com uma letra e não pode terminar em um traço</li>",
- "team_signup_url.next": "Próximo",
- "team_signup_url.regex": "Utilize apenas letras minúsculas, números e traços. Deve começar com uma letra e não pode terminar em um traço.",
- "team_signup_url.required": "Este campo é obrigatório",
- "team_signup_url.taken": "URL é usada ou contém uma palavra reservada",
- "team_signup_url.teamUrl": "Equipe URL",
- "team_signup_url.unavailable": "Está URL está indisponível. Por favor tente outra.",
- "team_signup_url.webAddress": "Escolha o endereço web para sua nova equipe:",
- "team_signup_username.back": "Voltar para o passo anterior",
- "team_signup_username.chooseUsername": "Escolha o seu nome de usuário",
- "team_signup_username.hint": "O nome de usuário precisa começar com uma letra, e conter entre {min} e {max} caracteres minúsculos contendo números, letras, e os símbolos '.', '-' e '_'",
- "team_signup_username.invalid": "O nome de usuário precisa começar com uma letra, e conter entre {min} e {max} caracteres no total, podendo ser números, letras minúsculas, ou qualquer dos símbolos '.', '-' ou '_'",
- "team_signup_username.memorable": "Escolha um nome de usuário memorizável que torna fácil para sua equipe de trabalho identificá-lo:",
- "team_signup_username.next": "Próximo",
- "team_signup_username.reserved": "Este nome de usuário é reservado, por favor, escolha uma nova.",
- "team_signup_username.username": "Seu usuário",
- "team_signup_welcome.address": "Endereço de E-mail",
- "team_signup_welcome.admin": "Sua conta irá administrar o novo site da equipe. <br />Você pode adicionar outros administradores depois.",
- "team_signup_welcome.confirm": "Por favor confirme seu endereço de e-mail:",
- "team_signup_welcome.different": "Utilize um e-mail diferente",
- "team_signup_welcome.instead": "Use este ao invez",
- "team_signup_welcome.lets": "Vamos configurar sua nova equipe",
- "team_signup_welcome.storageError": "Este serviço requer um armazenamento local para ser ativado. Por favor, habilite ou saia da navegação privada.",
- "team_signup_welcome.validEmailError": "Por favor entre um endereço de e-mail válido",
- "team_signup_welcome.welcome": "Bem-vindo:",
- "team_signup_welcome.yes": "Sim, este endereço de email está correto",
"textbox.bold": "**negrito**",
"textbox.edit": "Editar mensagem",
"textbox.help": "Ajuda",
diff --git a/webapp/package.json b/webapp/package.json
index 2e9efadb3..3ab971c09 100644
--- a/webapp/package.json
+++ b/webapp/package.json
@@ -28,7 +28,8 @@
"react-router": "2.0.1",
"react-textarea-autosize": "3.3.0",
"twemoji": "1.4.1",
- "velocity-animate": "1.2.3"
+ "velocity-animate": "1.2.3",
+ "superagent": "1.8.3"
},
"devDependencies": {
"babel-eslint": "5.0.0",
@@ -53,12 +54,22 @@
"sass-loader": "3.2.0",
"style-loader": "0.13.0",
"url-loader": "0.5.7",
- "webpack": "2.1.0-beta.5"
+ "webpack": "2.1.0-beta.5",
+
+ "mocha": "*",
+ "mocha-webpack": "*",
+ "webpack-node-externals": "*",
+ "mocha-jsdom": "*",
+ "jsdom": "*",
+ "jsdom-global": "*",
+ "react-addons-test-utils": "*",
+ "jquery-deferred": "*"
},
"scripts": {
"check": "eslint --ext \".jsx\" --ignore-pattern node_modules --quiet .",
"build": "webpack",
"run": "webpack --progress --watch",
- "run-fullmap": "webpack --progress --watch"
+ "run-fullmap": "webpack --progress --watch",
+ "test": "mocha-webpack --webpack-config webpack.config-test.js \"**/*.test.jsx\""
}
}
diff --git a/webapp/root.html b/webapp/root.html
index 1612bdce4..cc2b7cd61 100644
--- a/webapp/root.html
+++ b/webapp/root.html
@@ -46,7 +46,18 @@
</script>
</head>
<body>
- <div id='root'/>
+ <div id='root'>
+ <div
+ class='loading-screen'
+ style='relative'
+ >
+ <div class='loading__content'>
+ <div class='round round-1'></div>
+ <div class='round round-2'></div>
+ <div class='round round-3'></div>
+ </div>
+ </div>
+ </div>
<script>
window.setup_root();
</script>
diff --git a/webapp/root.jsx b/webapp/root.jsx
index 9268643f3..b0625438f 100644
--- a/webapp/root.jsx
+++ b/webapp/root.jsx
@@ -10,10 +10,10 @@ import 'sass/styles.scss';
import React from 'react';
import ReactDOM from 'react-dom';
-import {Router, Route, IndexRoute, IndexRedirect, Redirect, browserHistory} from 'react-router';
+import {Router, Route, IndexRoute, Redirect, browserHistory} from 'react-router';
import Root from 'components/root.jsx';
import LoggedIn from 'components/logged_in.jsx';
-import NotLoggedIn from 'components/not_logged_in.jsx';
+import HeaderFooterTemplate from 'components/header_footer_template.jsx';
import NeedsTeam from 'components/needs_team.jsx';
import PasswordResetSendLink from 'components/password_reset_send_link.jsx';
import PasswordResetForm from 'components/password_reset_form.jsx';
@@ -21,16 +21,15 @@ import ChannelView from 'components/channel_view.jsx';
import PermalinkView from 'components/permalink_view.jsx';
import Sidebar from 'components/sidebar.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
-import PreferenceStore from 'stores/preference_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import ErrorStore from 'stores/error_store.jsx';
-import BrowserStore from 'stores/browser_store.jsx';
-import SignupTeam from 'components/signup_team.jsx';
-import * as Client from 'utils/client.jsx';
-import * as Websockets from 'action_creators/websocket_actions.jsx';
+import TeamStore from 'stores/team_store.jsx';
import * as Utils from 'utils/utils.jsx';
+
+import Client from 'utils/web_client.jsx';
+
+import * as Websockets from 'action_creators/websocket_actions.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
-import SignupTeamConfirm from 'components/signup_team_confirm.jsx';
import SignupUserComplete from 'components/signup_user_complete.jsx';
import ShouldVerifyEmail from 'components/should_verify_email.jsx';
import DoVerifyEmail from 'components/do_verify_email.jsx';
@@ -47,14 +46,9 @@ import AddOutgoingWebhook from 'components/backstage/add_outgoing_webhook.jsx';
import AddCommand from 'components/backstage/add_command.jsx';
import ErrorPage from 'components/error_page.jsx';
-import SignupTeamComplete from 'components/signup_team_complete/components/signup_team_complete.jsx';
-import WelcomePage from 'components/signup_team_complete/components/team_signup_welcome_page.jsx';
-import TeamDisplayNamePage from 'components/signup_team_complete/components/team_signup_display_name_page.jsx';
-import TeamURLPage from 'components/signup_team_complete/components/team_signup_url_page.jsx';
-import SendInivtesPage from 'components/signup_team_complete/components/team_signup_send_invites_page.jsx';
-import UsernamePage from 'components/signup_team_complete/components/team_signup_username_page.jsx';
-import PasswordPage from 'components/signup_team_complete/components/team_signup_password_page.jsx';
-import FinishedPage from 'components/signup_team_complete/components/team_signup_finished.jsx';
+import AppDispatcher from './dispatcher/app_dispatcher.jsx';
+import Constants from './utils/constants.jsx';
+const ActionTypes = Constants.ActionTypes;
import Claim from 'components/claim/claim.jsx';
import EmailToOAuth from 'components/claim/components/email_to_oauth.jsx';
@@ -63,6 +57,10 @@ import LDAPToEmail from 'components/claim/components/ldap_to_email.jsx';
import EmailToLDAP from 'components/claim/components/email_to_ldap.jsx';
import Login from 'components/login/login.jsx';
+import SelectTeam from 'components/select_team/select_team.jsx';
+import CreateTeam from 'components/create_team/create_team.jsx';
+import CreateTeamDisplayName from 'components/create_team/components/display_name.jsx';
+import CreateTeamTeamUrl from 'components/create_team/components/team_url.jsx';
import * as I18n from 'i18n/i18n.jsx';
@@ -76,53 +74,33 @@ const notFoundParams = {
// This is for anything that needs to be done for ALL react components.
// This runs before we start to render anything.
function preRenderSetup(callwhendone) {
- const d1 = Client.getClientConfig(
- (data, textStatus, xhr) => {
- if (!data) {
- return;
- }
-
- global.window.mm_config = data;
-
- var serverVersion = xhr.getResponseHeader('X-Version-ID');
-
- if (serverVersion !== BrowserStore.getLastServerVersion()) {
- if (!BrowserStore.getLastServerVersion() || BrowserStore.getLastServerVersion() === '') {
- BrowserStore.setLastServerVersion(serverVersion);
- } else {
- BrowserStore.setLastServerVersion(serverVersion);
- window.location.reload(true);
- console.log('Detected version update refreshing the page'); //eslint-disable-line no-console
- }
- }
- },
- (err) => {
- AsyncClient.dispatchError(err, 'getClientConfig');
+ window.onerror = (msg, url, line, column, stack) => {
+ var l = {};
+ l.level = 'ERROR';
+ l.message = 'msg: ' + msg + ' row: ' + line + ' col: ' + column + ' stack: ' + stack + ' url: ' + url;
+
+ $.ajax({
+ url: '/api/v3/admin/log_client',
+ dataType: 'json',
+ contentType: 'application/json',
+ type: 'POST',
+ data: JSON.stringify(l)
+ });
+
+ if (window.mm_config && window.mm_config.EnableDeveloper === 'true') {
+ window.ErrorStore.storeLastError({message: 'DEVELOPER MODE: A javascript error has occured. Please use the javascript console to capture and report the error (row: ' + line + ' col: ' + column + ').'});
+ window.ErrorStore.emitChange();
}
- );
+ };
- const d2 = Client.getClientLicenceConfig(
- (data) => {
- if (!data) {
- return;
- }
+ var d1 = $.Deferred(); //eslint-disable-line new-cap
- global.window.mm_license = data;
- },
- (err) => {
- AsyncClient.dispatchError(err, 'getClientLicenceConfig');
+ GlobalActions.emitInitialLoad(
+ () => {
+ d1.resolve();
}
);
- // Set these here so they don't fail in client.jsx track
- global.window.analytics = [];
- global.window.analytics.page = () => {
- // Do Nothing
- };
- global.window.analytics.track = () => {
- // Do Nothing
- };
-
// Make sure the websockets close
$(window).on('beforeunload',
() => {
@@ -132,7 +110,9 @@ function preRenderSetup(callwhendone) {
function afterIntl() {
I18n.doAddLocaleData();
- $.when(d1, d2).done(callwhendone);
+ $.when(d1).done(() => {
+ callwhendone();
+ });
}
if (global.Intl) {
@@ -143,18 +123,59 @@ function preRenderSetup(callwhendone) {
}
function preLoggedIn(nextState, replace, callback) {
- const d1 = Client.getAllPreferences(
+ ErrorStore.clearLastError();
+ callback();
+}
+
+function preNeedsTeam(nextState, replace, callback) {
+ // First check to make sure you're in the current team
+ // for the current url.
+ var teamName = Utils.getTeamNameFromUrl();
+ var team = TeamStore.getByName(teamName);
+
+ if (!team) {
+ browserHistory.push('/error');
+ return;
+ }
+
+ GlobalActions.emitCloseRightHandSide();
+
+ TeamStore.saveMyTeam(team);
+ TeamStore.emitChange();
+
+ var d1 = $.Deferred(); //eslint-disable-line new-cap
+ var d2 = $.Deferred(); //eslint-disable-line new-cap
+
+ Client.getChannels(
(data) => {
- PreferenceStore.setPreferencesFromServer(data);
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_CHANNELS,
+ channels: data.channels,
+ members: data.members
+ });
+
+ d1.resolve();
},
(err) => {
- AsyncClient.dispatchError(err, 'getAllPreferences');
+ AsyncClient.dispatchError(err, 'getChannels');
+ d1.resolve();
}
);
- const d2 = AsyncClient.getChannels();
+ Client.getProfiles(
+ (data) => {
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_PROFILES,
+ profiles: data
+ });
- ErrorStore.clearLastError();
+ d2.resolve();
+ },
+ (err) => {
+ AsyncClient.dispatchError(err, 'getProfiles');
+ d2.resolve();
+ }
+ );
$.when(d1, d2).done(() => {
callback();
@@ -163,21 +184,20 @@ function preLoggedIn(nextState, replace, callback) {
function onPermalinkEnter(nextState) {
const postId = nextState.params.postid;
-
GlobalActions.emitPostFocusEvent(postId);
}
-function onChannelEnter(nextState) {
- doChannelChange(nextState);
+function onChannelEnter(nextState, replace) {
+ doChannelChange(nextState, replace);
}
-function onChannelChange(prevState, nextState) {
+function onChannelChange(prevState, nextState, replace) {
if (prevState.params.channel !== nextState.params.channel) {
- doChannelChange(nextState);
+ doChannelChange(nextState, replace);
}
}
-function doChannelChange(state) {
+function doChannelChange(state, replace) {
let channel;
if (state.location.query.fakechannel) {
channel = JSON.parse(state.location.query.fakechannel);
@@ -187,28 +207,13 @@ function doChannelChange(state) {
channel = ChannelStore.getMoreByName(state.params.channel);
}
if (!channel) {
- console.error('Unable to get channel to change to.'); //eslint-disable-line no-console
+ replace('/');
+ return;
}
}
GlobalActions.emitChannelClickEvent(channel);
}
-function onLoggedOut(nextState) {
- const teamName = nextState.params.team;
- Client.logout(
- () => {
- browserHistory.push('/' + teamName + '/login');
- BrowserStore.signalLogout();
- BrowserStore.clear();
- ErrorStore.clearLastError();
- PreferenceStore.clear();
- },
- () => {
- browserHistory.push('/' + teamName + '/login');
- }
- );
-}
-
function renderRootComponent() {
ReactDOM.render((
<Router
@@ -222,141 +227,38 @@ function renderRootComponent() {
path='error'
component={ErrorPage}
/>
- <Route
- component={LoggedIn}
- onEnter={preLoggedIn}
- >
+ <Route component={HeaderFooterTemplate}>
<Route
- path=':team/channels/:channel'
- onEnter={onChannelEnter}
- onChange={onChannelChange}
- components={{
- sidebar: Sidebar,
- center: ChannelView
- }}
+ path='login'
+ component={Login}
/>
<Route
- path=':team/pl/:postid'
- onEnter={onPermalinkEnter}
- components={{
- sidebar: Sidebar,
- center: PermalinkView
- }}
- />
- <Route
- path=':team/tutorial'
- components={{
- sidebar: Sidebar,
- center: TutorialView
- }}
- />
- <Route
- path=':team/logout'
- onEnter={onLoggedOut}
- />
- <Route path='settings/integrations'>
- <IndexRoute
- components={{
- navbar: BackstageNavbar,
- sidebar: BackstageSidebar,
- center: Integrations
- }}
- />
- <Route path='incoming_webhooks'>
- <IndexRoute
- components={{
- navbar: BackstageNavbar,
- sidebar: BackstageSidebar,
- center: InstalledIncomingWebhooks
- }}
- />
- <Route
- path='add'
- components={{
- navbar: BackstageNavbar,
- sidebar: BackstageSidebar,
- center: AddIncomingWebhook
- }}
- />
- </Route>
- <Route path='outgoing_webhooks'>
- <IndexRoute
- components={{
- navbar: BackstageNavbar,
- sidebar: BackstageSidebar,
- center: InstalledOutgoingWebhooks
- }}
- />
- <Route
- path='add'
- components={{
- navbar: BackstageNavbar,
- sidebar: BackstageSidebar,
- center: AddOutgoingWebhook
- }}
- />
- </Route>
- <Route path='commands'>
- <IndexRoute
- components={{
- navbar: BackstageNavbar,
- sidebar: BackstageSidebar,
- center: InstalledCommands
- }}
- />
- <Route
- path='add'
- components={{
- navbar: BackstageNavbar,
- sidebar: BackstageSidebar,
- center: AddCommand
- }}
- />
- </Route>
- <Redirect
- from='*'
- to='/error'
- query={notFoundParams}
- />
- </Route>
- <Route
- path='admin_console'
- component={AdminConsole}
+ path='reset_password'
+ component={PasswordResetSendLink}
/>
- </Route>
- <Route component={NotLoggedIn}>
<Route
- path='signup_team'
- component={SignupTeam}
+ path='reset_password_complete'
+ component={PasswordResetForm}
/>
<Route
- path='signup_team_complete'
- component={SignupTeamComplete}
+ path='claim'
+ component={Claim}
>
- <IndexRoute component={FinishedPage}/>
- <Route
- path='welcome'
- component={WelcomePage}
- />
- <Route
- path='team_display_name'
- component={TeamDisplayNamePage}
- />
<Route
- path='team_url'
- component={TeamURLPage}
+ path='oauth_to_email'
+ component={OAuthToEmail}
/>
<Route
- path='send_invites'
- component={SendInivtesPage}
+ path='email_to_oauth'
+ component={EmailToOAuth}
/>
<Route
- path='username'
- component={UsernamePage}
+ path='email_to_ldap'
+ component={EmailToLDAP}
/>
<Route
- path='password'
- component={PasswordPage}
+ path='ldap_to_email'
+ component={LDAPToEmail}
/>
</Route>
<Route
@@ -364,10 +266,6 @@ function renderRootComponent() {
component={SignupUserComplete}
/>
<Route
- path='signup_team_confirm'
- component={SignupTeamConfirm}
- />
- <Route
path='should_verify_email'
component={ShouldVerifyEmail}
/>
@@ -375,51 +273,136 @@ function renderRootComponent() {
path='do_verify_email'
component={DoVerifyEmail}
/>
+ </Route>
+ <Route
+ component={LoggedIn}
+ onEnter={preLoggedIn}
+ >
+ <Route component={HeaderFooterTemplate}>
+ <Route
+ path='select_team'
+ component={SelectTeam}
+ />
+ <Route
+ path='create_team'
+ component={CreateTeam}
+ >
+ <IndexRoute component={CreateTeamDisplayName}/>
+ <Route
+ path='display_name'
+ component={CreateTeamDisplayName}
+ />
+ <Route
+ path='team_url'
+ component={CreateTeamTeamUrl}
+ />
+ </Route>
+ </Route>
+ <Route
+ path='admin_console'
+ component={AdminConsole}
+ />
<Route
path=':team'
component={NeedsTeam}
+ onEnter={preNeedsTeam}
>
- <IndexRedirect to='login'/>
<Route
- path='login'
- component={Login}
+ path='channels/:channel'
+ onEnter={onChannelEnter}
+ onChange={onChannelChange}
+ components={{
+ sidebar: Sidebar,
+ center: ChannelView
+ }}
/>
<Route
- path='reset_password'
- component={PasswordResetSendLink}
+ path='pl/:postid'
+ onEnter={onPermalinkEnter}
+ components={{
+ sidebar: Sidebar,
+ center: PermalinkView
+ }}
/>
<Route
- path='reset_password_complete'
- component={PasswordResetForm}
+ path='tutorial'
+ components={{
+ sidebar: Sidebar,
+ center: TutorialView
+ }}
/>
- <Route
- path='claim'
- component={Claim}
- >
- <Route
- path='oauth_to_email'
- component={OAuthToEmail}
- />
- <Route
- path='email_to_oauth'
- component={EmailToOAuth}
- />
- <Route
- path='email_to_ldap'
- component={EmailToLDAP}
+ <Route path='settings/integrations'>
+ <IndexRoute
+ components={{
+ navbar: BackstageNavbar,
+ sidebar: BackstageSidebar,
+ center: Integrations
+ }}
/>
- <Route
- path='ldap_to_email'
- component={LDAPToEmail}
+ <Route path='incoming_webhooks'>
+ <IndexRoute
+ components={{
+ navbar: BackstageNavbar,
+ sidebar: BackstageSidebar,
+ center: InstalledIncomingWebhooks
+ }}
+ />
+ <Route
+ path='add'
+ components={{
+ navbar: BackstageNavbar,
+ sidebar: BackstageSidebar,
+ center: AddIncomingWebhook
+ }}
+ />
+ </Route>
+ <Route path='outgoing_webhooks'>
+ <IndexRoute
+ components={{
+ navbar: BackstageNavbar,
+ sidebar: BackstageSidebar,
+ center: InstalledOutgoingWebhooks
+ }}
+ />
+ <Route
+ path='add'
+ components={{
+ navbar: BackstageNavbar,
+ sidebar: BackstageSidebar,
+ center: AddOutgoingWebhook
+ }}
+ />
+ </Route>
+ <Route path='commands'>
+ <IndexRoute
+ components={{
+ navbar: BackstageNavbar,
+ sidebar: BackstageSidebar,
+ center: InstalledCommands
+ }}
+ />
+ <Route
+ path='add'
+ components={{
+ navbar: BackstageNavbar,
+ sidebar: BackstageSidebar,
+ center: AddCommand
+ }}
+ />
+ </Route>
+ <Redirect
+ from='*'
+ to='/error'
+ query={notFoundParams}
/>
</Route>
- <Redirect
- from='*'
- to='/error'
- query={notFoundParams}
- />
</Route>
</Route>
+ <Redirect
+ from='*'
+ to='/error'
+ query={notFoundParams}
+ />
</Route>
</Router>
),
diff --git a/webapp/sass/routes/_backstage.scss b/webapp/sass/routes/_backstage.scss
index 3257f6582..ebfe97ee4 100644
--- a/webapp/sass/routes/_backstage.scss
+++ b/webapp/sass/routes/_backstage.scss
@@ -216,6 +216,7 @@ body {
.item-details__description,
.item-details__token,
.item-details__trigger-words,
+ .item-details__url,
.item-details__creation {
display: inline-block;
margin-top: 10px;
diff --git a/webapp/sass/routes/_signup.scss b/webapp/sass/routes/_signup.scss
index 77ccdf4ed..08bd0d12d 100644
--- a/webapp/sass/routes/_signup.scss
+++ b/webapp/sass/routes/_signup.scss
@@ -419,6 +419,17 @@
}
}
+ .signup-team-dir-err {
+ background: #fafafa;
+ border-top: 1px solid #d5d5d5;
+ color: inherit;
+ padding: 5px 15px;
+
+ &:first-child {
+ border: none;
+ }
+ }
+
.signup-team-dir__name {
float: left;
overflow: hidden;
diff --git a/webapp/stores/browser_store.jsx b/webapp/stores/browser_store.jsx
index d605aac80..2dae78f46 100644
--- a/webapp/stores/browser_store.jsx
+++ b/webapp/stores/browser_store.jsx
@@ -35,18 +35,6 @@ class BrowserStoreClass {
this.isSignallingLogin = this.isSignallingLogin.bind(this);
}
- checkVersion() {
- var currentVersion = this.getGlobalItem('storage_version');
- if (currentVersion !== global.window.mm_config.Version) {
- this.clearAll();
- try {
- this.setGlobalItem('storage_version', global.window.mm_config.Version);
- } catch (e) {
- // Do nothing
- }
- }
- }
-
setItem(name, value) {
this.setGlobalItem(getPrefix() + name, value);
}
diff --git a/webapp/stores/error_store.jsx b/webapp/stores/error_store.jsx
index 715029185..3e043dd78 100644
--- a/webapp/stores/error_store.jsx
+++ b/webapp/stores/error_store.jsx
@@ -20,6 +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;
+ }
+
+ getIgnoreEmailPreview() {
+ return this.ignore_email_preview;
}
emitChange() {
@@ -57,6 +63,11 @@ class ErrorStoreClass extends EventEmitter {
}
clearLastError() {
+ var lastError = this.getLastError();
+ if (lastError && lastError.email_preview) {
+ this.ignore_email_preview = true;
+ }
+
BrowserStore.removeGlobalItem('last_error');
BrowserStore.removeGlobalItem('last_error_conn');
this.emitChange();
diff --git a/webapp/stores/preference_store.jsx b/webapp/stores/preference_store.jsx
index fcfd1c426..1a461f39f 100644
--- a/webapp/stores/preference_store.jsx
+++ b/webapp/stores/preference_store.jsx
@@ -112,4 +112,4 @@ class PreferenceStoreClass extends EventEmitter {
const PreferenceStore = new PreferenceStoreClass();
export default PreferenceStore;
-window.PreferenceStore = PreferenceStore;
+global.window.PreferenceStore = PreferenceStore;
diff --git a/webapp/stores/team_store.jsx b/webapp/stores/team_store.jsx
index e1fc9167d..356df7b07 100644
--- a/webapp/stores/team_store.jsx
+++ b/webapp/stores/team_store.jsx
@@ -33,7 +33,14 @@ class TeamStoreClass extends EventEmitter {
this.getCurrentInviteLink = this.getCurrentInviteLink.bind(this);
this.saveTeam = this.saveTeam.bind(this);
+ this.clear();
+ }
+
+ clear() {
this.teams = {};
+ this.team_members = [];
+ this.members_for_team = [];
+ this.teamListings = {};
this.currentTeamId = '';
}
@@ -119,6 +126,34 @@ class TeamStoreClass extends EventEmitter {
this.saveTeam(team);
this.currentTeamId = team.id;
}
+
+ saveTeamMembers(members) {
+ this.team_members = members;
+ }
+
+ appendTeamMember(member) {
+ this.team_members.push(member);
+ }
+
+ getTeamMembers() {
+ return this.team_members;
+ }
+
+ saveMembersForTeam(members) {
+ this.members_for_team = members;
+ }
+
+ getMembersForTeam() {
+ return this.members_for_team;
+ }
+
+ saveTeamListings(teams) {
+ this.teamListings = teams;
+ }
+
+ getTeamListings() {
+ return this.teamListings;
+ }
}
var TeamStore = new TeamStoreClass();
@@ -135,6 +170,18 @@ TeamStore.dispatchToken = AppDispatcher.register((payload) => {
TeamStore.saveTeams(action.teams);
TeamStore.emitChange();
break;
+ case ActionTypes.RECEIVED_TEAM_MEMBERS:
+ TeamStore.saveTeamMembers(action.team_members);
+ TeamStore.emitChange();
+ break;
+ case ActionTypes.RECEIVED_ALL_TEAM_LISTINGS:
+ TeamStore.saveTeamListings(action.teams);
+ TeamStore.emitChange();
+ break;
+ case ActionTypes.RECEIVED_MEMBERS_FOR_TEAM:
+ TeamStore.saveMembersForTeam(action.team_members);
+ TeamStore.emitChange();
+ break;
default:
}
});
diff --git a/webapp/stores/user_store.jsx b/webapp/stores/user_store.jsx
index 4213e6e8c..2c6bfade3 100644
--- a/webapp/stores/user_store.jsx
+++ b/webapp/stores/user_store.jsx
@@ -16,11 +16,17 @@ const CHANGE_EVENT_STATUSES = 'change_statuses';
class UserStoreClass extends EventEmitter {
constructor() {
super();
+ this.clear();
+ }
+
+ clear() {
this.profiles = {};
+ this.direct_profiles = {};
this.statuses = {};
this.sessions = {};
this.audits = {};
this.currentUserId = '';
+ this.noAccounts = false;
}
emitChange(userId) {
@@ -116,7 +122,12 @@ class UserStoreClass extends EventEmitter {
return this.getCurrentUser();
}
- return this.getProfiles()[userId];
+ const user = this.getProfiles()[userId];
+ if (user) {
+ return user;
+ }
+
+ return this.getDirectProfiles()[userId];
}
getProfileByUsername(username) {
@@ -137,6 +148,14 @@ class UserStoreClass extends EventEmitter {
return profileUsernameMap;
}
+ getDirectProfiles() {
+ return this.direct_profiles;
+ }
+
+ saveDirectProfiles(profiles) {
+ this.direct_profiles = profiles;
+ }
+
getProfiles() {
return this.profiles;
}
@@ -259,6 +278,14 @@ class UserStoreClass extends EventEmitter {
getStatus(id) {
return this.getStatuses()[id];
}
+
+ getNoAccounts() {
+ return this.noAccounts;
+ }
+
+ setNoAccounts(noAccounts) {
+ this.noAccounts = noAccounts;
+ }
}
var UserStore = new UserStoreClass();
@@ -272,6 +299,10 @@ UserStore.dispatchToken = AppDispatcher.register((payload) => {
UserStore.saveProfiles(action.profiles);
UserStore.emitChange();
break;
+ case ActionTypes.RECEIVED_DIRECT_PROFILES:
+ UserStore.saveDirectProfiles(action.profiles);
+ UserStore.emitChange();
+ break;
case ActionTypes.RECEIVED_ME:
UserStore.setCurrentUser(action.me);
UserStore.emitChange(action.me.id);
diff --git a/webapp/tests/client_channel.test.jsx b/webapp/tests/client_channel.test.jsx
new file mode 100644
index 000000000..b8374123c
--- /dev/null
+++ b/webapp/tests/client_channel.test.jsx
@@ -0,0 +1,334 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+/* eslint-disable no-console */
+/* eslint-disable global-require */
+/* eslint-disable func-names */
+/* eslint-disable prefer-arrow-callback */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable no-unreachable */
+
+import assert from 'assert';
+import TestHelper from './test_helper.jsx';
+
+describe('Client.Channels', function() {
+ this.timeout(100000);
+
+ it('createChannel', function(done) {
+ TestHelper.initBasic(() => {
+ var channel = TestHelper.fakeChannel();
+ channel.team_id = TestHelper.basicTeam().id;
+ TestHelper.basicClient().createChannel(
+ channel,
+ function(data) {
+ assert.equal(data.id.length > 0, true);
+ assert.equal(data.name, channel.name);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('createDirectChannel', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().createUser(
+ TestHelper.fakeUser(),
+ function(user2) {
+ TestHelper.basicClient().addUserToTeam(
+ user2.id,
+ function() {
+ TestHelper.basicClient().createDirectChannel(
+ user2.id,
+ function(data) {
+ assert.equal(data.id.length > 0, true);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('updateChannel', function(done) {
+ TestHelper.initBasic(() => {
+ var channel = TestHelper.basicChannel();
+ channel.display_name = 'changed';
+ TestHelper.basicClient().updateChannel(
+ channel,
+ function(data) {
+ assert.equal(data.id.length > 0, true);
+ assert.equal(data.display_name, 'changed');
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('updateChannelHeader', function(done) {
+ TestHelper.initBasic(() => {
+ var channel = TestHelper.basicChannel();
+ channel.display_name = 'changed';
+ TestHelper.basicClient().updateChannelHeader(
+ channel.id,
+ 'new header',
+ function(data) {
+ assert.equal(data.id.length > 0, true);
+ assert.equal(data.header, 'new header');
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('updateChannelPurpose', function(done) {
+ TestHelper.initBasic(() => {
+ var channel = TestHelper.basicChannel();
+ channel.display_name = 'changed';
+ TestHelper.basicClient().updateChannelPurpose(
+ channel.id,
+ 'new purpose',
+ function(data) {
+ assert.equal(data.id.length > 0, true);
+ assert.equal(data.purpose, 'new purpose');
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('updateChannelNotifyProps', function(done) {
+ TestHelper.initBasic(() => {
+ var props = {};
+ props.channel_id = TestHelper.basicChannel().id;
+ props.user_id = TestHelper.basicUser().id;
+ props.desktop = 'all';
+ TestHelper.basicClient().updateChannelNotifyProps(
+ props,
+ function(data) {
+ assert.equal(data.desktop, 'all');
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('leaveChannel', function(done) {
+ TestHelper.initBasic(() => {
+ var channel = TestHelper.basicChannel();
+ TestHelper.basicClient().leaveChannel(
+ channel.id,
+ function(data) {
+ assert.equal(data.id, channel.id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('joinChannel', function(done) {
+ TestHelper.initBasic(() => {
+ var channel = TestHelper.basicChannel();
+ TestHelper.basicClient().leaveChannel(
+ channel.id,
+ function() {
+ TestHelper.basicClient().joinChannel(
+ channel.id,
+ function(data) {
+ assert.equal(data.id, channel.id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('deleteChannel', function(done) {
+ TestHelper.initBasic(() => {
+ var channel = TestHelper.basicChannel();
+ TestHelper.basicClient().deleteChannel(
+ channel.id,
+ function(data) {
+ assert.equal(data.id, channel.id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('updateLastViewedAt', function(done) {
+ TestHelper.initBasic(() => {
+ var channel = TestHelper.basicChannel();
+ TestHelper.basicClient().updateLastViewedAt(
+ channel.id,
+ function(data) {
+ assert.equal(data.id, channel.id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getChannels', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getChannels(
+ function(data) {
+ assert.equal(data.channels.length, 3);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getChannel', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getChannel(
+ TestHelper.basicChannel().id,
+ function(data) {
+ assert.equal(TestHelper.basicChannel().id, data.channel.id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getMoreChannels', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getMoreChannels(
+ function(data) {
+ assert.equal(data.channels.length, 0);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getChannelCounts', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getChannelCounts(
+ function(data) {
+ assert.equal(data.counts[TestHelper.basicChannel().id], 1);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getChannelExtraInfo', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getChannelExtraInfo(
+ TestHelper.basicChannel().id,
+ 5,
+ function(data) {
+ assert.equal(data.member_count, 1);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('addChannelMember', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().createUser(
+ TestHelper.fakeUser(),
+ function(user2) {
+ TestHelper.basicClient().addUserToTeam(
+ user2.id,
+ function() {
+ TestHelper.basicClient().addChannelMember(
+ TestHelper.basicChannel().id,
+ user2.id,
+ function(data) {
+ assert.equal(data.channel_id.length > 0, true);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('removeChannelMember', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().removeChannelMember(
+ TestHelper.basicChannel().id,
+ TestHelper.basicUser().id,
+ function(data) {
+ assert.equal(data.channel_id.length > 0, true);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+});
+
diff --git a/webapp/tests/client_command.test.jsx b/webapp/tests/client_command.test.jsx
new file mode 100644
index 000000000..f7f0d2b25
--- /dev/null
+++ b/webapp/tests/client_command.test.jsx
@@ -0,0 +1,123 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+/* eslint-disable no-console */
+/* eslint-disable global-require */
+/* eslint-disable func-names */
+/* eslint-disable prefer-arrow-callback */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable no-unreachable */
+
+import assert from 'assert';
+import TestHelper from './test_helper.jsx';
+
+describe('Client.Commands', function() {
+ this.timeout(100000);
+
+ it('listCommands', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().listCommands(
+ function(data) {
+ assert.equal(data.length > 0, true);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('listTeamCommands', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().listTeamCommands(
+ function() {
+ done(new Error('cmds not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.command.disabled.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('executeCommand', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().executeCommand(
+ TestHelper.basicChannel().id,
+ '/shrug',
+ null,
+ function(data) {
+ assert.equal(data.response_type, 'in_channel');
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('addCommand', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+
+ var cmd = {};
+ cmd.url = 'http://www.gonowhere.com';
+ cmd.trigger = '/hello';
+ cmd.method = 'P';
+ cmd.username = '';
+ cmd.icon_url = '';
+ cmd.auto_complete = false;
+ cmd.auto_complete_desc = '';
+ cmd.auto_complete_hint = '';
+ cmd.display_name = 'Unit Test';
+
+ TestHelper.basicClient().addCommand(
+ cmd,
+ function() {
+ done(new Error('cmds not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.command.disabled.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('deleteCommand', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().deleteCommand(
+ TestHelper.generateId(),
+ function() {
+ done(new Error('cmds not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.command.disabled.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('regenCommandToken', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().regenCommandToken(
+ TestHelper.generateId(),
+ function() {
+ done(new Error('cmds not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.command.disabled.app_error');
+ done();
+ }
+ );
+ });
+ });
+});
+
diff --git a/webapp/tests/client_general.test.jsx b/webapp/tests/client_general.test.jsx
new file mode 100644
index 000000000..870c11257
--- /dev/null
+++ b/webapp/tests/client_general.test.jsx
@@ -0,0 +1,333 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+/* eslint-disable no-console */
+/* eslint-disable global-require */
+/* eslint-disable func-names */
+/* eslint-disable prefer-arrow-callback */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable no-unreachable */
+
+var assert = require('assert');
+import TestHelper from './test_helper.jsx';
+
+describe('Client.General', function() {
+ this.timeout(10000);
+
+ it('Admin.getClientConfig', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getClientConfig(
+ function(data) {
+ assert.equal(data.SiteName, 'Mattermost');
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('Admin.getComplianceReports', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().getComplianceReports(
+ function() {
+ done(new Error('should need system admin permissions'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.context.system_permissions.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('Admin.saveComplianceReports', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+
+ var job = {};
+ job.desc = 'desc';
+ job.emails = '';
+ job.keywords = 'test';
+ job.start_at = new Date();
+ job.end_at = new Date();
+
+ TestHelper.basicClient().saveComplianceReports(
+ job,
+ function() {
+ done(new Error('should need system admin permissions'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.context.system_permissions.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('Admin.getLogs', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().getLogs(
+ function() {
+ done(new Error('should need system admin permissions'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.context.system_permissions.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('Admin.getServerAudits', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().getServerAudits(
+ function() {
+ done(new Error('should need system admin permissions'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.context.system_permissions.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('Admin.getConfig', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().getConfig(
+ function() {
+ done(new Error('should need system admin permissions'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.context.system_permissions.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('Admin.getAnalytics', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().getAnalytics(
+ 'standard',
+ null,
+ function() {
+ done(new Error('should need system admin permissions'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.context.system_permissions.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('Admin.getTeamAnalytics', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().getTeamAnalytics(
+ TestHelper.basicTeam().id,
+ 'standard',
+ function() {
+ done(new Error('should need system admin permissions'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.context.system_permissions.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('Admin.saveConfig', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ var config = {};
+ config.site_name = 'test';
+
+ TestHelper.basicClient().saveConfig(
+ config,
+ function() {
+ done(new Error('should need system admin permissions'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.context.system_permissions.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('Admin.testEmail', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ var config = {};
+ config.site_name = 'test';
+
+ TestHelper.basicClient().testEmail(
+ config,
+ function() {
+ done(new Error('should need system admin permissions'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.context.system_permissions.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('Admin.logClientError', function(done) {
+ TestHelper.initBasic(() => {
+ var config = {};
+ config.site_name = 'test';
+ TestHelper.basicClient().logClientError('this is a test');
+ done();
+ });
+ });
+
+ it('Admin.adminResetMfa', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+
+ TestHelper.basicClient().adminResetMfa(
+ TestHelper.basicUser().id,
+ function() {
+ done(new Error('should need a license'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.context.permissions.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('Admin.adminResetPassword', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ var user = TestHelper.basicUser();
+
+ TestHelper.basicClient().resetPassword(
+ user.id,
+ 'new_password',
+ function() {
+ throw Error('shouldnt work');
+ },
+ function(err) {
+ // this should fail since you're not a system admin
+ assert.equal(err.id, 'api.context.invalid_param.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('License.getClientLicenceConfig', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getClientLicenceConfig(
+ function(data) {
+ assert.equal(data.IsLicensed, 'false');
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('License.removeLicenseFile', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().removeLicenseFile(
+ function() {
+ done(new Error('not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.context.permissions.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ /*it('License.uploadLicenseFile', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().uploadLicenseFile(
+ 'form data',
+ function() {
+ done(new Error('not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.context.permissions.app_error');
+ done();
+ }
+ );
+ });
+ });*/
+
+ // TODO XXX FIX ME - this test depends on make dist
+
+ // it('General.getTranslations', function(done) {
+ // TestHelper.initBasic(() => {
+ // TestHelper.basicClient().getTranslations(
+ // 'http://localhost:8065/static/i18n/es.json',
+ // function(data) {
+ // assert.equal(data['login.or'], 'o');
+ // done();
+ // },
+ // function(err) {
+ // done(new Error(err.message));
+ // }
+ // );
+ // });
+ // });
+
+ it('File.getFileInfo', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+
+ TestHelper.basicClient().getFileInfo(
+ `/${TestHelper.basicChannel().id}/${TestHelper.basicUser().id}/filename.txt`,
+ function(data) {
+ assert.equal(data.filename, 'filename.txt');
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('File.getPublicLink', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ var data = {};
+ data.channel_id = TestHelper.basicChannel().id;
+ data.user_id = TestHelper.basicUser().id;
+ data.filename = `/${TestHelper.basicChannel().id}/${TestHelper.basicUser().id}/filename.txt`;
+
+ TestHelper.basicClient().getPublicLink(
+ data,
+ function() {
+ done(new Error('not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.file.get_public_link.disabled.app_error');
+ done();
+ }
+ );
+ });
+ });
+});
+
diff --git a/webapp/tests/client_hooks.test.jsx b/webapp/tests/client_hooks.test.jsx
new file mode 100644
index 000000000..0cad22153
--- /dev/null
+++ b/webapp/tests/client_hooks.test.jsx
@@ -0,0 +1,139 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+/* eslint-disable no-console */
+/* eslint-disable global-require */
+/* eslint-disable func-names */
+/* eslint-disable prefer-arrow-callback */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable no-unreachable */
+
+import assert from 'assert';
+import TestHelper from './test_helper.jsx';
+
+describe('Client.Hooks', function() {
+ this.timeout(100000);
+
+ it('addIncomingHook', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+
+ var hook = {};
+ hook.channel_id = TestHelper.basicChannel().id;
+ hook.description = 'desc';
+ hook.display_name = 'Unit Test';
+
+ TestHelper.basicClient().addIncomingHook(
+ hook,
+ function() {
+ done(new Error('hooks not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.webhook.create_incoming.disabled.app_errror');
+ done();
+ }
+ );
+ });
+ });
+
+ it('deleteIncomingHook', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().deleteIncomingHook(
+ TestHelper.generateId(),
+ function() {
+ done(new Error('hooks not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.webhook.delete_incoming.disabled.app_errror');
+ done();
+ }
+ );
+ });
+ });
+
+ it('listIncomingHooks', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().listIncomingHooks(
+ function() {
+ done(new Error('hooks not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.webhook.get_incoming.disabled.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('addOutgoingHook', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+
+ var hook = {};
+ hook.channel_id = TestHelper.basicChannel().id;
+ hook.description = 'desc';
+ hook.display_name = 'Unit Test';
+
+ TestHelper.basicClient().addOutgoingHook(
+ hook,
+ function() {
+ done(new Error('hooks not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.webhook.create_outgoing.disabled.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('deleteOutgoingHook', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().deleteOutgoingHook(
+ TestHelper.generateId(),
+ function() {
+ done(new Error('hooks not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.webhook.delete_outgoing.disabled.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('listOutgoingHooks', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().listOutgoingHooks(
+ function() {
+ done(new Error('hooks not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.webhook.get_outgoing.disabled.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('regenOutgoingHookToken', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().regenOutgoingHookToken(
+ TestHelper.generateId(),
+ function() {
+ done(new Error('hooks not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.webhook.regen_outgoing_token.disabled.app_error');
+ done();
+ }
+ );
+ });
+ });
+});
+
diff --git a/webapp/tests/client_oauth.test.jsx b/webapp/tests/client_oauth.test.jsx
new file mode 100644
index 000000000..df2fc665b
--- /dev/null
+++ b/webapp/tests/client_oauth.test.jsx
@@ -0,0 +1,60 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+/* eslint-disable no-console */
+/* eslint-disable global-require */
+/* eslint-disable func-names */
+/* eslint-disable prefer-arrow-callback */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable no-unreachable */
+
+import assert from 'assert';
+import TestHelper from './test_helper.jsx';
+
+describe('Client.OAuth', function() {
+ this.timeout(100000);
+
+ it('registerOAuthApp', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+
+ var app = {};
+ app.name = 'test';
+ app.homepage = 'homepage';
+ app.description = 'desc';
+ app.callback_urls = '';
+
+ TestHelper.basicClient().registerOAuthApp(
+ app,
+ function() {
+ done(new Error('not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.oauth.register_oauth_app.turn_off.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('allowOAuth2', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+
+ TestHelper.basicClient().allowOAuth2(
+ 'GET',
+ '123456',
+ 'http://nowhere.com',
+ 'state',
+ 'scope',
+ function() {
+ done(new Error('not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.oauth.allow_oauth.turn_off.app_error');
+ done();
+ }
+ );
+ });
+ });
+});
diff --git a/webapp/tests/client_post.test.jsx b/webapp/tests/client_post.test.jsx
new file mode 100644
index 000000000..db48e4000
--- /dev/null
+++ b/webapp/tests/client_post.test.jsx
@@ -0,0 +1,207 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+/* eslint-disable no-console */
+/* eslint-disable global-require */
+/* eslint-disable func-names */
+/* eslint-disable prefer-arrow-callback */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable no-unreachable */
+
+import assert from 'assert';
+import TestHelper from './test_helper.jsx';
+
+describe('Client.Posts', function() {
+ this.timeout(100000);
+
+ it('createPost', function(done) {
+ TestHelper.initBasic(() => {
+ var post = TestHelper.fakePost();
+ post.channel_id = TestHelper.basicChannel().id;
+
+ TestHelper.basicClient().createPost(
+ post,
+ function(data) {
+ assert.equal(data.id.length > 0, true);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getPostById', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getPostById(
+ TestHelper.basicPost().id,
+ function(data) {
+ assert.equal(data.order[0], TestHelper.basicPost().id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getPost', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getPost(
+ TestHelper.basicChannel().id,
+ TestHelper.basicPost().id,
+ function(data) {
+ assert.equal(data.order[0], TestHelper.basicPost().id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('updatePost', function(done) {
+ TestHelper.initBasic(() => {
+ var post = TestHelper.basicPost();
+ post.message = 'new message';
+ post.channel_id = TestHelper.basicChannel().id;
+
+ TestHelper.basicClient().updatePost(
+ post,
+ function(data) {
+ assert.equal(data.id.length > 0, true);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('deletePost', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().deletePost(
+ TestHelper.basicChannel().id,
+ TestHelper.basicPost().id,
+ function(data) {
+ assert.equal(data.id, TestHelper.basicPost().id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('searchPost', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().search(
+ 'unit test',
+ function(data) {
+ assert.equal(data.order[0], TestHelper.basicPost().id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getPostsPage', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getPostsPage(
+ TestHelper.basicChannel().id,
+ 0,
+ 10,
+ function(data) {
+ assert.equal(data.order[0], TestHelper.basicPost().id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getPosts', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getPosts(
+ TestHelper.basicChannel().id,
+ 0,
+ function(data) {
+ assert.equal(data.order[0], TestHelper.basicPost().id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getPostsBefore', function(done) {
+ TestHelper.initBasic(() => {
+ var post = TestHelper.fakePost();
+ post.channel_id = TestHelper.basicChannel().id;
+
+ TestHelper.basicClient().createPost(
+ post,
+ function(rpost) {
+ TestHelper.basicClient().getPostsBefore(
+ TestHelper.basicChannel().id,
+ rpost.id,
+ 0,
+ 10,
+ function(data) {
+ assert.equal(data.order[0], TestHelper.basicPost().id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getPostsAfter', function(done) {
+ TestHelper.initBasic(() => {
+ var post = TestHelper.fakePost();
+ post.channel_id = TestHelper.basicChannel().id;
+
+ TestHelper.basicClient().createPost(
+ post,
+ function(rpost) {
+ TestHelper.basicClient().getPostsAfter(
+ TestHelper.basicChannel().id,
+ TestHelper.basicPost().id,
+ 0,
+ 10,
+ function(data) {
+ assert.equal(data.order[0], rpost.id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+});
+
diff --git a/webapp/tests/client_preferences.test.jsx b/webapp/tests/client_preferences.test.jsx
new file mode 100644
index 000000000..987728704
--- /dev/null
+++ b/webapp/tests/client_preferences.test.jsx
@@ -0,0 +1,72 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+/* eslint-disable no-console */
+/* eslint-disable global-require */
+/* eslint-disable func-names */
+/* eslint-disable prefer-arrow-callback */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable no-unreachable */
+
+import assert from 'assert';
+import TestHelper from './test_helper.jsx';
+
+describe('Client.Preferences', function() {
+ this.timeout(100000);
+
+ it('getAllPreferences', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getAllPreferences(
+ function(data) {
+ assert.equal(data[0].category, 'tutorial_step');
+ assert.equal(data[0].user_id, TestHelper.basicUser().id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('savePreferences', function(done) {
+ TestHelper.initBasic(() => {
+ var perf = {};
+ perf.user_id = TestHelper.basicUser().id;
+ perf.category = 'test';
+ perf.name = 'name';
+ perf.value = 'value';
+
+ var perfs = [];
+ perfs.push(perf);
+
+ TestHelper.basicClient().savePreferences(
+ perfs,
+ function(data) {
+ assert.equal(data, true);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getPreferenceCategory', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getPreferenceCategory(
+ 'tutorial_step',
+ function(data) {
+ assert.equal(data[0].category, 'tutorial_step');
+ assert.equal(data[0].user_id, TestHelper.basicUser().id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+});
+
diff --git a/webapp/tests/client_team.test.jsx b/webapp/tests/client_team.test.jsx
new file mode 100644
index 000000000..e8b71d2f8
--- /dev/null
+++ b/webapp/tests/client_team.test.jsx
@@ -0,0 +1,235 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+/* eslint-disable no-console */
+/* eslint-disable global-require */
+/* eslint-disable func-names */
+/* eslint-disable prefer-arrow-callback */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable no-unreachable */
+
+import assert from 'assert';
+import TestHelper from './test_helper.jsx';
+
+describe('Client.Team', function() {
+ this.timeout(100000);
+
+ it('findTeamByName', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().findTeamByName(
+ TestHelper.basicTeam().name,
+ function(data) {
+ assert.equal(data, true);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('signupTeam', function(done) {
+ var client = TestHelper.createClient();
+ var email = TestHelper.fakeEmail();
+
+ client.signupTeam(
+ email,
+ function(data) {
+ assert.equal(data.email, email);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+
+ it('createTeamFromSignup', function(done) {
+ var client = TestHelper.createClient();
+ var email = TestHelper.fakeEmail();
+
+ client.signupTeam(
+ email,
+ function(data) {
+ var teamSignup = {};
+ teamSignup.invites = [];
+ teamSignup.data = decodeURIComponent(data.follow_link.split('&h=')[0].replace('/signup_team_complete/?d=', ''));
+ teamSignup.hash = decodeURIComponent(data.follow_link.split('&h=')[1]);
+
+ teamSignup.user = TestHelper.fakeUser();
+ teamSignup.team = TestHelper.fakeTeam();
+ teamSignup.team.email = teamSignup.user.email;
+
+ client.createTeamFromSignup(
+ teamSignup,
+ function(data2) {
+ assert.equal(data2.team.id.length > 0, true);
+ assert.equal(data2.user.id.length > 0, true);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+
+ it('createTeam', function(done) {
+ var client = TestHelper.createClient();
+ var team = TestHelper.fakeTeam();
+ client.createTeam(
+ team,
+ function(data) {
+ assert.equal(data.id.length > 0, true);
+ assert.equal(data.name, team.name);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+
+ it('getAllTeams', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getAllTeams(
+ function(data) {
+ assert.equal(data[TestHelper.basicTeam().id].name, TestHelper.basicTeam().name);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getAllTeamListings', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getAllTeamListings(
+ function(data) {
+ console.log(data);
+ assert.equal(data != null, true);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getMyTeam', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getMyTeam(
+ function(data) {
+ assert.equal(data.name, TestHelper.basicTeam().name);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('GetTeamMembers', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getTeamMembers(
+ TestHelper.basicTeam().id,
+ function(data) {
+ assert.equal(data.length > 0, true);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('inviteMembers', function(done) {
+ TestHelper.initBasic(() => {
+ var data = {};
+ data.invites = [];
+ var invite = {};
+ invite.email = TestHelper.fakeEmail();
+ invite.firstName = 'first';
+ invite.lastName = 'last';
+ data.invites.push(invite);
+
+ TestHelper.basicClient().inviteMembers(
+ data,
+ function(dataBack) {
+ assert.equal(dataBack.invites.length, 1);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('updateTeam', function(done) {
+ TestHelper.initBasic(() => {
+ var team = TestHelper.basicTeam();
+ team.display_name = 'test_updated';
+
+ TestHelper.basicClient().updateTeam(
+ team,
+ function(data) {
+ assert.equal(data.display_name, 'test_updated');
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('addUserToTeam', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().createUser(
+ TestHelper.fakeUser(),
+ function(user2) {
+ TestHelper.basicClient().addUserToTeam(
+ user2.id,
+ function(data) {
+ assert.equal(data.user_id, user2.id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getInviteInfo', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getInviteInfo(
+ TestHelper.basicTeam().invite_id,
+ function(data) {
+ assert.equal(data.display_name.length > 0, true);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+});
+
diff --git a/webapp/tests/client_user.test.jsx b/webapp/tests/client_user.test.jsx
new file mode 100644
index 000000000..e0ead2de9
--- /dev/null
+++ b/webapp/tests/client_user.test.jsx
@@ -0,0 +1,550 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+/* eslint-disable no-console */
+/* eslint-disable global-require */
+/* eslint-disable func-names */
+/* eslint-disable prefer-arrow-callback */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable no-unreachable */
+
+import assert from 'assert';
+import TestHelper from './test_helper.jsx';
+
+describe('Client.User', function() {
+ this.timeout(100000);
+
+ it('getMe', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getMe(
+ function(data) {
+ assert.equal(data.id, TestHelper.basicUser().id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getInitialLoad', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getInitialLoad(
+ function(data) {
+ assert.equal(data.user.id.length > 0, true);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('createUser', function(done) {
+ var client = TestHelper.createClient();
+ var user = TestHelper.fakeUser();
+ client.createUser(
+ user,
+ function(data) {
+ assert.equal(data.id.length > 0, true);
+ assert.equal(data.email, user.email);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+
+ it('loginByEmail', function(done) {
+ var client = TestHelper.createClient();
+ var user = TestHelper.fakeUser();
+ client.createUser(
+ user,
+ function() {
+ client.login(
+ user.email,
+ null,
+ user.password,
+ null,
+ function(data) {
+ assert.equal(data.id.length > 0, true);
+ assert.equal(data.email, user.email);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+
+ it('loginByUsername', function(done) {
+ var client = TestHelper.createClient();
+ var user = TestHelper.fakeUser();
+ client.createUser(
+ user,
+ function() {
+ client.login(
+ null,
+ user.username,
+ user.password,
+ null,
+ function(data) {
+ assert.equal(data.id.length > 0, true);
+ assert.equal(data.email, user.email);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+
+ it('loginByLdap', function(done) {
+ var client = TestHelper.createClient();
+ client.enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ var user = TestHelper.fakeUser();
+ client.createUser(
+ user,
+ function() {
+ client.loginByLdap(
+ user.username,
+ user.password,
+ null,
+ function() {
+ done(new Error());
+ },
+ function(err) {
+ assert.equal(err.id, 'api.user.login_ldap.disabled.app_error');
+ done();
+ }
+ );
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+
+ it('updateUser', function(done) {
+ TestHelper.initBasic(() => {
+ var user = TestHelper.basicUser();
+ user.nickname = 'updated';
+
+ TestHelper.basicClient().updateUser(
+ user,
+ function(data) {
+ assert.equal(data.nickname, 'updated');
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('updatePassword', function(done) {
+ TestHelper.initBasic(() => {
+ var user = TestHelper.basicUser();
+
+ TestHelper.basicClient().updatePassword(
+ user.id,
+ user.password,
+ 'update_password',
+ function(data) {
+ assert.equal(data.user_id, user.id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('updateUserNotifyProps', function(done) {
+ TestHelper.initBasic(() => {
+ var user = TestHelper.basicUser();
+
+ var notifyProps = {
+ all: 'true',
+ channel: 'true',
+ desktop: 'all',
+ desktop_sound: 'true',
+ email: 'false',
+ first_name: 'false',
+ mention_keys: '',
+ user_id: user.id
+ };
+
+ TestHelper.basicClient().updateUserNotifyProps(
+ notifyProps,
+ function(data) {
+ assert.equal(data.notify_props.email, 'false');
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('updateRoles', function(done) {
+ TestHelper.initBasic(() => {
+ var user = TestHelper.basicUser();
+
+ TestHelper.basicClient().updateRoles(
+ user.id,
+ '',
+ function(data) {
+ assert.equal(data.roles, '');
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('updateActive', function(done) {
+ TestHelper.initBasic(() => {
+ var user = TestHelper.basicUser();
+
+ TestHelper.basicClient().updateActive(
+ user.id,
+ false,
+ function(data) {
+ assert.equal(data.last_activity_at > 0, true);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('sendPasswordReset', function(done) {
+ TestHelper.initBasic(() => {
+ var user = TestHelper.basicUser();
+
+ TestHelper.basicClient().sendPasswordReset(
+ user.email,
+ function(data) {
+ assert.equal(data.email, user.email);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('resetPassword', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+
+ TestHelper.basicClient().resetPassword(
+ '',
+ 'new_password',
+ function() {
+ throw Error('shouldnt work');
+ },
+ function(err) {
+ // this should fail since you're not a system admin
+ assert.equal(err.id, 'api.context.invalid_param.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('emailToOAuth', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ var user = TestHelper.basicUser();
+
+ TestHelper.basicClient().emailToOAuth(
+ user.email,
+ 'new_password',
+ 'gitlab',
+ function() {
+ throw Error('shouldnt work');
+ },
+ function(err) {
+ // this should fail since you're not a system admin
+ assert.equal(err.id, 'api.user.check_user_password.invalid.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('oauthToEmail', function(done) {
+ TestHelper.initBasic(() => {
+ var user = TestHelper.basicUser();
+
+ TestHelper.basicClient().oauthToEmail(
+ user.email,
+ 'new_password',
+ function(data) {
+ assert.equal(data.follow_link.length > 0, true);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('emailToLdap', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ var user = TestHelper.basicUser();
+
+ TestHelper.basicClient().emailToLdap(
+ user.email,
+ user.password,
+ 'unknown_id',
+ 'unknown_pwd',
+ function() {
+ throw Error('shouldnt work');
+ },
+ function(err) {
+ assert.equal(err.id, 'ent.ldap.do_login.licence_disable.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('ldapToEmail', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ var user = TestHelper.basicUser();
+
+ TestHelper.basicClient().ldapToEmail(
+ user.email,
+ 'new_password',
+ 'new_password',
+ function() {
+ throw Error('shouldnt work');
+ },
+ function(err) {
+ assert.equal(err.id, 'api.user.ldap_to_email.not_ldap_account.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('logout', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().logout(
+ function(data) {
+ assert.equal(data.user_id, TestHelper.basicUser().id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('checkMfa', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().checkMfa(
+ 'email',
+ TestHelper.generateId(),
+ function(data) {
+ assert.equal(data.mfa_required, 'false');
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getSessions', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getSessions(
+ TestHelper.basicUser().id,
+ function(data) {
+ assert.equal(data[0].user_id, TestHelper.basicUser().id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('revokeSession', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getSessions(
+ TestHelper.basicUser().id,
+ function(sessions) {
+ TestHelper.basicClient().revokeSession(
+ sessions[0].id,
+ function(data) {
+ assert.equal(data.id, sessions[0].id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getAudits', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getAudits(
+ TestHelper.basicUser().id,
+ function(data) {
+ assert.equal(data[0].user_id, TestHelper.basicUser().id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getDirectProfiles', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getDirectProfiles(
+ function(data) {
+ assert.equal(Object.keys(data).length === 0, true);
+ done();
+ },
+ function(err) {
+ done(new Error(err.getDirectProfiles));
+ }
+ );
+ });
+ });
+
+ it('getProfiles', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getProfiles(
+ function(data) {
+ assert.equal(data[TestHelper.basicUser().id].id, TestHelper.basicUser().id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getProfilesForTeam', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().getProfilesForTeam(
+ TestHelper.basicTeam().id,
+ function(data) {
+ assert.equal(data[TestHelper.basicUser().id].id, TestHelper.basicUser().id);
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('getStatuses', function(done) {
+ TestHelper.initBasic(() => {
+ var ids = [];
+ ids.push(TestHelper.basicUser().id);
+
+ TestHelper.basicClient().getStatuses(
+ ids,
+ function(data) {
+ assert.equal(data[TestHelper.basicUser().id], 'online');
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('verifyEmail', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().verifyEmail(
+ 'junk',
+ 'junk',
+ function() {
+ done(new Error('should be invalid'));
+ },
+ function(err) {
+ assert.equal(err.id, 'api.context.invalid_param.app_error');
+ done();
+ }
+ );
+ });
+ });
+
+ it('resendVerification', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().resendVerification(
+ TestHelper.basicUser().email,
+ function() {
+ done();
+ },
+ function(err) {
+ done(new Error(err.message));
+ }
+ );
+ });
+ });
+
+ it('updateMfa', function(done) {
+ TestHelper.initBasic(() => {
+ TestHelper.basicClient().enableLogErrorsToConsole(false); // Disabling since this unit test causes an error
+ TestHelper.basicClient().updateMfa(
+ 'junk',
+ true,
+ function() {
+ done(new Error('not enabled'));
+ },
+ function(err) {
+ assert.equal(err.id, 'ent.mfa.license_disable.app_error');
+ done();
+ }
+ );
+ });
+ });
+});
diff --git a/webapp/tests/spinner_button.test.jsx b/webapp/tests/spinner_button.test.jsx
new file mode 100644
index 000000000..0e282e0ee
--- /dev/null
+++ b/webapp/tests/spinner_button.test.jsx
@@ -0,0 +1,24 @@
+/* eslint-disable no-console */
+/* eslint-disable global-require */
+/* eslint-disable func-names */
+/* eslint-disable prefer-arrow-callback */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable no-unreachable */
+
+var jsdom = require('mocha-jsdom');
+var assert = require('assert');
+import TestUtils from 'react-addons-test-utils';
+import SpinnerButton from '../components/spinner_button.jsx';
+import React from 'react';
+
+describe('SpinnerButton', function() {
+ jsdom();
+
+ it('check props', function() {
+ const spinner = TestUtils.renderIntoDocument(
+ <SpinnerButton spinning={false}/>
+ );
+
+ assert.equal(spinner.props.spinning, false, 'should start in the default false state');
+ });
+}); \ No newline at end of file
diff --git a/webapp/tests/test_helper.jsx b/webapp/tests/test_helper.jsx
new file mode 100644
index 000000000..385279360
--- /dev/null
+++ b/webapp/tests/test_helper.jsx
@@ -0,0 +1,183 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+/* eslint-disable no-console */
+/* eslint-disable global-require */
+/* eslint-disable func-names */
+/* eslint-disable prefer-arrow-callback */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable no-unreachable */
+/* eslint-disable new-cap */
+
+import Client from '../client/client.jsx';
+import jqd from 'jquery-deferred';
+
+class TestHelperClass {
+ basicClient = () => {
+ return this.basicc;
+ }
+
+ basicTeam = () => {
+ return this.basict;
+ }
+
+ basicUser = () => {
+ return this.basicu;
+ }
+
+ basicChannel = () => {
+ return this.basicch;
+ }
+
+ basicPost = () => {
+ return this.basicp;
+ }
+
+ generateId = () => {
+ // implementation taken from http://stackoverflow.com/a/2117523
+ var id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
+
+ id = id.replace(/[xy]/g, function replaceRandom(c) {
+ var r = Math.floor(Math.random() * 16);
+
+ var v;
+ if (c === 'x') {
+ v = r;
+ } else {
+ v = r & 0x3 | 0x8;
+ }
+
+ return v.toString(16);
+ });
+
+ return 'uid' + id;
+ }
+
+ createClient() {
+ var c = new Client();
+ c.setUrl('http://localhost:8065');
+ c.useHeaderToken();
+ c.enableLogErrorsToConsole(true);
+ return c;
+ }
+
+ fakeEmail = () => {
+ return 'success' + this.generateId() + '@simulator.amazonses.com';
+ }
+
+ fakeUser = () => {
+ var user = {};
+ user.email = this.fakeEmail();
+ user.allow_marketing = true;
+ user.password = 'password1';
+ user.username = this.generateId();
+ return user;
+ }
+
+ fakeTeam = () => {
+ var team = {};
+ team.name = this.generateId();
+ team.display_name = `Unit Test ${team.name}`;
+ team.type = 'O';
+ team.email = this.fakeEmail();
+ team.allowed_domains = '';
+ return team;
+ }
+
+ fakeChannel = () => {
+ var channel = {};
+ channel.name = this.generateId();
+ channel.display_name = `Unit Test ${channel.name}`;
+ channel.type = 'O'; // open channel
+ return channel;
+ }
+
+ fakePost = () => {
+ var post = {};
+ post.message = `Unit Test ${this.generateId()}`;
+ return post;
+ }
+
+ initBasic = (callback) => {
+ this.basicc = this.createClient();
+
+ var d1 = jqd.Deferred();
+ var email = this.fakeEmail();
+ var outer = this; // eslint-disable-line consistent-this
+
+ this.basicClient().signupTeam(
+ email,
+ function(rsignUp) {
+ var teamSignup = {};
+ teamSignup.invites = [];
+ teamSignup.data = decodeURIComponent(rsignUp.follow_link.split('&h=')[0].replace('/signup_team_complete/?d=', ''));
+ teamSignup.hash = decodeURIComponent(rsignUp.follow_link.split('&h=')[1]);
+
+ teamSignup.user = outer.fakeUser();
+ teamSignup.team = outer.fakeTeam();
+ teamSignup.team.email = email;
+ teamSignup.user.email = email;
+ var password = teamSignup.user.password;
+
+ outer.basicClient().createTeamFromSignup(
+ teamSignup,
+ function(rteamSignup) {
+ outer.basict = rteamSignup.team;
+ outer.basicu = rteamSignup.user;
+ outer.basicu.password = password;
+ outer.basicClient().setTeamId(outer.basict.id);
+ outer.basicClient().login(
+ rteamSignup.user.email,
+ null,
+ password,
+ null,
+ function() {
+ outer.basicClient().useHeaderToken();
+ var channel = outer.fakeChannel();
+ channel.team_id = outer.basicTeam().id;
+ outer.basicClient().createChannel(
+ channel,
+ function(rchannel) {
+ outer.basicch = rchannel;
+ var post = outer.fakePost();
+ post.channel_id = rchannel.id;
+
+ outer.basicClient().createPost(
+ post,
+ function(rpost) {
+ outer.basicp = rpost;
+ d1.resolve();
+ },
+ function(err) {
+ throw err;
+ }
+ );
+ },
+ function(err) {
+ throw err;
+ }
+ );
+ },
+ function(err) {
+ throw err;
+ }
+ );
+ },
+ function(err) {
+ throw err;
+ }
+ );
+ },
+ function(err) {
+ throw err;
+ }
+ );
+
+ jqd.when(d1).done(() => {
+ callback();
+ });
+ }
+}
+
+var TestHelper = new TestHelperClass();
+export default TestHelper;
diff --git a/webapp/utils/async_client.jsx b/webapp/utils/async_client.jsx
index 80a08dc21..189b159e8 100644
--- a/webapp/utils/async_client.jsx
+++ b/webapp/utils/async_client.jsx
@@ -2,7 +2,7 @@
// See License.txt for license information.
import $ from 'jquery';
-import * as client from './client.jsx';
+import Client from './web_client.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import BrowserStore from 'stores/browser_store.jsx';
@@ -11,6 +11,7 @@ import PreferenceStore from 'stores/preference_store.jsx';
import PostStore from 'stores/post_store.jsx';
import UserStore from 'stores/user_store.jsx';
import * as utils from './utils.jsx';
+import ErrorStore from 'stores/error_store.jsx';
import Constants from './constants.jsx';
const ActionTypes = Constants.ActionTypes;
@@ -19,6 +20,8 @@ const StatTypes = Constants.StatTypes;
// Used to track in progress async calls
const callTracker = {};
+const ASYNC_CLIENT_TIMEOUT = 5000;
+
export function dispatchError(err, method) {
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_ERROR,
@@ -36,7 +39,7 @@ function isCallInProgress(callName) {
return false;
}
- if (utils.getTimestamp() - callTracker[callName] > 5000) {
+ if (utils.getTimestamp() - callTracker[callName] > ASYNC_CLIENT_TIMEOUT) {
//console.log('AsyncClient call ' + callName + ' expired after more than 5 seconds');
return false;
}
@@ -51,16 +54,12 @@ export function getChannels(checkVersion) {
callTracker.getChannels = utils.getTimestamp();
- return client.getChannels(
- (data, textStatus, xhr) => {
+ return Client.getChannels(
+ (data) => {
callTracker.getChannels = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
if (checkVersion) {
- var serverVersion = xhr.getResponseHeader('X-Version-ID');
+ var serverVersion = Client.getServerVersion();
if (serverVersion !== BrowserStore.getLastServerVersion()) {
if (!BrowserStore.getLastServerVersion() || BrowserStore.getLastServerVersion() === '') {
@@ -93,14 +92,10 @@ export function getChannel(id) {
callTracker['getChannel' + id] = utils.getTimestamp();
- client.getChannel(id,
- (data, textStatus, xhr) => {
+ Client.getChannel(id,
+ (data) => {
callTracker['getChannel' + id] = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_CHANNEL,
channel: data.channel,
@@ -131,13 +126,17 @@ export function updateLastViewedAt(id) {
}
callTracker[`updateLastViewed${channelId}`] = utils.getTimestamp();
- client.updateLastViewedAt(
+ Client.updateLastViewedAt(
channelId,
() => {
callTracker.updateLastViewed = 0;
+ ErrorStore.clearLastError();
+ ErrorStore.emitChange();
},
(err) => {
callTracker.updateLastViewed = 0;
+ var count = ErrorStore.getConnectionErrorCount();
+ ErrorStore.setConnectionErrorCount(count + 1);
dispatchError(err, 'updateLastViewedAt');
}
);
@@ -150,21 +149,17 @@ export function getMoreChannels(force) {
if (ChannelStore.getMoreAll().loading || force) {
callTracker.getMoreChannels = utils.getTimestamp();
- client.getMoreChannels(
- function getMoreChannelsSuccess(data, textStatus, xhr) {
+ Client.getMoreChannels(
+ (data) => {
callTracker.getMoreChannels = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_MORE_CHANNELS,
channels: data.channels,
members: data.members
});
},
- function getMoreChannelsFailure(err) {
+ (err) => {
callTracker.getMoreChannels = 0;
dispatchError(err, 'getMoreChannels');
}
@@ -187,16 +182,12 @@ export function getChannelExtraInfo(id, memberLimit) {
callTracker['getChannelExtraInfo_' + channelId] = utils.getTimestamp();
- client.getChannelExtraInfo(
+ Client.getChannelExtraInfo(
channelId,
memberLimit,
- (data, textStatus, xhr) => {
+ (data) => {
callTracker['getChannelExtraInfo_' + channelId] = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_CHANNEL_EXTRA_INFO,
extra_info: data
@@ -210,53 +201,90 @@ export function getChannelExtraInfo(id, memberLimit) {
}
}
+export function getTeamMembers(teamId) {
+ if (isCallInProgress('getTeamMembers')) {
+ return;
+ }
+
+ callTracker.getTeamMembers = utils.getTimestamp();
+ Client.getTeamMembers(
+ teamId,
+ (data) => {
+ callTracker.getTeamMembers = 0;
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_MEMBERS_FOR_TEAM,
+ team_members: data
+ });
+ },
+ (err) => {
+ callTracker.getTeamMembers = 0;
+ dispatchError(err, 'getTeamMembers');
+ }
+ );
+}
+
export function getProfiles() {
if (isCallInProgress('getProfiles')) {
return;
}
callTracker.getProfiles = utils.getTimestamp();
- client.getProfiles(
- function getProfilesSuccess(data, textStatus, xhr) {
+ Client.getProfiles(
+ (data) => {
callTracker.getProfiles = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_PROFILES,
profiles: data
});
},
- function getProfilesFailure(err) {
+ (err) => {
callTracker.getProfiles = 0;
dispatchError(err, 'getProfiles');
}
);
}
+export function getDirectProfiles() {
+ if (isCallInProgress('getDirectProfiles')) {
+ return;
+ }
+
+ callTracker.getDirectProfiles = utils.getTimestamp();
+ Client.getDirectProfiles(
+ (data) => {
+ callTracker.getDirectProfiles = 0;
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_DIRECT_PROFILES,
+ profiles: data
+ });
+ },
+ (err) => {
+ callTracker.getDirectProfiles = 0;
+ dispatchError(err, 'getDirectProfiles');
+ }
+ );
+}
+
export function getSessions() {
if (isCallInProgress('getSessions')) {
return;
}
callTracker.getSessions = utils.getTimestamp();
- client.getSessions(
+ Client.getSessions(
UserStore.getCurrentId(),
- function getSessionsSuccess(data, textStatus, xhr) {
+ (data) => {
callTracker.getSessions = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_SESSIONS,
sessions: data
});
},
- function getSessionsFailure(err) {
+ (err) => {
callTracker.getSessions = 0;
dispatchError(err, 'getSessions');
}
@@ -269,21 +297,17 @@ export function getAudits() {
}
callTracker.getAudits = utils.getTimestamp();
- client.getAudits(
+ Client.getAudits(
UserStore.getCurrentId(),
- function getAuditsSuccess(data, textStatus, xhr) {
+ (data) => {
callTracker.getAudits = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_AUDITS,
audits: data
});
},
- function getAuditsFailure(err) {
+ (err) => {
callTracker.getAudits = 0;
dispatchError(err, 'getAudits');
}
@@ -296,14 +320,10 @@ export function getLogs() {
}
callTracker.getLogs = utils.getTimestamp();
- client.getLogs(
- (data, textStatus, xhr) => {
+ Client.getLogs(
+ (data) => {
callTracker.getLogs = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_LOGS,
logs: data
@@ -322,14 +342,10 @@ export function getServerAudits() {
}
callTracker.getServerAudits = utils.getTimestamp();
- client.getServerAudits(
- (data, textStatus, xhr) => {
+ Client.getServerAudits(
+ (data) => {
callTracker.getServerAudits = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_SERVER_AUDITS,
audits: data
@@ -348,14 +364,10 @@ export function getComplianceReports() {
}
callTracker.getComplianceReports = utils.getTimestamp();
- client.getComplianceReports(
- (data, textStatus, xhr) => {
+ Client.getComplianceReports(
+ (data) => {
callTracker.getComplianceReports = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_SERVER_COMPLIANCE_REPORTS,
complianceReports: data
@@ -374,14 +386,10 @@ export function getConfig() {
}
callTracker.getConfig = utils.getTimestamp();
- client.getConfig(
- (data, textStatus, xhr) => {
+ Client.getConfig(
+ (data) => {
callTracker.getConfig = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_CONFIG,
config: data
@@ -400,14 +408,10 @@ export function getAllTeams() {
}
callTracker.getAllTeams = utils.getTimestamp();
- client.getAllTeams(
- (data, textStatus, xhr) => {
+ Client.getAllTeams(
+ (data) => {
callTracker.getAllTeams = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_ALL_TEAMS,
teams: data
@@ -420,27 +424,45 @@ export function getAllTeams() {
);
}
+export function getAllTeamListings() {
+ if (isCallInProgress('getAllTeamListings')) {
+ return;
+ }
+
+ callTracker.getAllTeamListings = utils.getTimestamp();
+ Client.getAllTeamListings(
+ (data) => {
+ callTracker.getAllTeamListings = 0;
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_ALL_TEAM_LISTINGS,
+ teams: data
+ });
+ },
+ (err) => {
+ callTracker.getAllTeams = 0;
+ dispatchError(err, 'getAllTeamListings');
+ }
+ );
+}
+
export function search(terms) {
if (isCallInProgress('search_' + String(terms))) {
return;
}
callTracker['search_' + String(terms)] = utils.getTimestamp();
- client.search(
+ Client.search(
terms,
- function searchSuccess(data, textStatus, xhr) {
+ (data) => {
callTracker['search_' + String(terms)] = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_SEARCH,
results: data
});
},
- function searchFailure(err) {
+ (err) => {
callTracker['search_' + String(terms)] = 0;
dispatchError(err, 'search');
}
@@ -478,15 +500,11 @@ export function getPostsPage(id, maxPosts) {
if (channelId != null) {
callTracker['getPostsPage_' + channelId] = utils.getTimestamp();
- client.getPostsPage(
+ Client.getPostsPage(
channelId,
0,
numPosts,
- (data, textStatus, xhr) => {
- if (xhr.status === 304 || !data) {
- return;
- }
-
+ (data) => {
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_POSTS,
id: channelId,
@@ -536,14 +554,10 @@ export function getPosts(id) {
callTracker['getPosts_' + channelId] = utils.getTimestamp();
- client.getPosts(
+ Client.getPosts(
channelId,
latestPostTime,
- (data, textStatus, xhr) => {
- if (xhr.status === 304 || !data) {
- return;
- }
-
+ (data) => {
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_POSTS,
id: channelId,
@@ -573,16 +587,12 @@ export function getPostsBefore(postId, offset, numPost) {
return;
}
- client.getPostsBefore(
+ Client.getPostsBefore(
channelId,
postId,
offset,
numPost,
- (data, textStatus, xhr) => {
- if (xhr.status === 304 || !data) {
- return;
- }
-
+ (data) => {
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_POSTS,
id: channelId,
@@ -612,16 +622,12 @@ export function getPostsAfter(postId, offset, numPost) {
return;
}
- client.getPostsAfter(
+ Client.getPostsAfter(
channelId,
postId,
offset,
numPost,
- (data, textStatus, xhr) => {
- if (xhr.status === 304 || !data) {
- return;
- }
-
+ (data) => {
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_POSTS,
id: channelId,
@@ -647,14 +653,10 @@ export function getMe() {
}
callTracker.getMe = utils.getTimestamp();
- return client.getMe(
- (data, textStatus, xhr) => {
+ return Client.getMe(
+ (data) => {
callTracker.getMe = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_ME,
me: data
@@ -684,14 +686,10 @@ export function getStatuses() {
}
callTracker.getStatuses = utils.getTimestamp();
- client.getStatuses(teammateIds,
- (data, textStatus, xhr) => {
+ Client.getStatuses(teammateIds,
+ (data) => {
callTracker.getStatuses = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_STATUSES,
statuses: data
@@ -710,20 +708,16 @@ export function getMyTeam() {
}
callTracker.getMyTeam = utils.getTimestamp();
- return client.getMyTeam(
- function getMyTeamSuccess(data, textStatus, xhr) {
+ return Client.getMyTeam(
+ (data) => {
callTracker.getMyTeam = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_MY_TEAM,
team: data
});
},
- function getMyTeamFailure(err) {
+ (err) => {
callTracker.getMyTeam = 0;
dispatchError(err, 'getMyTeam');
}
@@ -736,14 +730,10 @@ export function getAllPreferences() {
}
callTracker.getAllPreferences = utils.getTimestamp();
- client.getAllPreferences(
- (data, textStatus, xhr) => {
+ Client.getAllPreferences(
+ (data) => {
callTracker.getAllPreferences = 0;
- if (xhr.status === 304 || !data) {
- return;
- }
-
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_PREFERENCES,
preferences: data
@@ -768,15 +758,13 @@ export function savePreference(category, name, value, success, error) {
}
export function savePreferences(preferences, success, error) {
- client.savePreferences(
+ Client.savePreferences(
preferences,
- (data, textStatus, xhr) => {
- if (xhr.status !== 304) {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECEIVED_PREFERENCES,
- preferences
- });
- }
+ (data) => {
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_PREFERENCES,
+ preferences
+ });
if (success) {
success(data);
@@ -793,12 +781,12 @@ export function savePreferences(preferences, success, error) {
}
export function getSuggestedCommands(command, suggestionId, component) {
- client.listCommands(
+ Client.listCommands(
(data) => {
var matches = [];
data.forEach((cmd) => {
if (('/' + cmd.trigger).indexOf(command) === 0) {
- let s = '/' + cmd.trigger;
+ const s = '/' + cmd.trigger;
let hint = '';
if (cmd.auto_complete_hint && cmd.auto_complete_hint.length !== 0) {
hint = cmd.auto_complete_hint;
@@ -842,7 +830,7 @@ export function getFileInfo(filename) {
callTracker[callName] = utils.getTimestamp();
- client.getFileInfo(
+ Client.getFileInfo(
filename,
(data) => {
callTracker[callName] = 0;
@@ -870,7 +858,7 @@ export function getStandardAnalytics(teamId) {
callTracker[callName] = utils.getTimestamp();
- client.getAnalytics(
+ Client.getAnalytics(
'standard',
teamId,
(data) => {
@@ -923,7 +911,7 @@ export function getAdvancedAnalytics(teamId) {
callTracker[callName] = utils.getTimestamp();
- client.getAnalytics(
+ Client.getAnalytics(
'extra_counts',
teamId,
(data) => {
@@ -980,7 +968,7 @@ export function getPostsPerDayAnalytics(teamId) {
callTracker[callName] = utils.getTimestamp();
- client.getAnalytics(
+ Client.getAnalytics(
'post_counts_day',
teamId,
(data) => {
@@ -1014,7 +1002,7 @@ export function getUsersPerDayAnalytics(teamId) {
callTracker[callName] = utils.getTimestamp();
- client.getAnalytics(
+ Client.getAnalytics(
'user_counts_with_posts_day',
teamId,
(data) => {
@@ -1048,7 +1036,7 @@ export function getRecentAndNewUsersAnalytics(teamId) {
callTracker[callName] = utils.getTimestamp();
- client.getProfilesForTeam(
+ Client.getProfilesForTeam(
teamId,
(users) => {
const stats = {};
@@ -1129,7 +1117,7 @@ export function listIncomingHooks() {
callTracker.listIncomingHooks = utils.getTimestamp();
- client.listIncomingHooks(
+ Client.listIncomingHooks(
(data) => {
callTracker.listIncomingHooks = 0;
@@ -1152,7 +1140,7 @@ export function listOutgoingHooks() {
callTracker.listOutgoingHooks = utils.getTimestamp();
- client.listOutgoingHooks(
+ Client.listOutgoingHooks(
(data) => {
callTracker.listOutgoingHooks = 0;
@@ -1169,7 +1157,7 @@ export function listOutgoingHooks() {
}
export function addIncomingHook(hook, success, error) {
- client.addIncomingHook(
+ Client.addIncomingHook(
hook,
(data) => {
AppDispatcher.handleServerAction({
@@ -1192,7 +1180,7 @@ export function addIncomingHook(hook, success, error) {
}
export function addOutgoingHook(hook, success, error) {
- client.addOutgoingHook(
+ Client.addOutgoingHook(
hook,
(data) => {
AppDispatcher.handleServerAction({
@@ -1215,8 +1203,8 @@ export function addOutgoingHook(hook, success, error) {
}
export function deleteIncomingHook(id) {
- client.deleteIncomingHook(
- {id},
+ Client.deleteIncomingHook(
+ id,
() => {
AppDispatcher.handleServerAction({
type: ActionTypes.REMOVED_INCOMING_WEBHOOK,
@@ -1230,8 +1218,8 @@ export function deleteIncomingHook(id) {
}
export function deleteOutgoingHook(id) {
- client.deleteOutgoingHook(
- {id},
+ Client.deleteOutgoingHook(
+ id,
() => {
AppDispatcher.handleServerAction({
type: ActionTypes.REMOVED_OUTGOING_WEBHOOK,
@@ -1245,8 +1233,8 @@ export function deleteOutgoingHook(id) {
}
export function regenOutgoingHookToken(id) {
- client.regenOutgoingHookToken(
- {id},
+ Client.regenOutgoingHookToken(
+ id,
(data) => {
AppDispatcher.handleServerAction({
type: ActionTypes.UPDATED_OUTGOING_WEBHOOK,
@@ -1266,7 +1254,7 @@ export function listTeamCommands() {
callTracker.listTeamCommands = utils.getTimestamp();
- client.listTeamCommands(
+ Client.listTeamCommands(
(data) => {
callTracker.listTeamCommands = 0;
@@ -1283,7 +1271,7 @@ export function listTeamCommands() {
}
export function addCommand(command, success, error) {
- client.addCommand(
+ Client.addCommand(
command,
(data) => {
AppDispatcher.handleServerAction({
@@ -1306,8 +1294,8 @@ export function addCommand(command, success, error) {
}
export function deleteCommand(id) {
- client.deleteCommand(
- {id},
+ Client.deleteCommand(
+ id,
() => {
AppDispatcher.handleServerAction({
type: ActionTypes.REMOVED_COMMAND,
@@ -1321,8 +1309,8 @@ export function deleteCommand(id) {
}
export function regenCommandToken(id) {
- client.regenCommandToken(
- {id},
+ Client.regenCommandToken(
+ id,
(data) => {
AppDispatcher.handleServerAction({
type: ActionTypes.UPDATED_COMMAND,
diff --git a/webapp/utils/channel_intro_messages.jsx b/webapp/utils/channel_intro_messages.jsx
index ddd615581..1d18e26ba 100644
--- a/webapp/utils/channel_intro_messages.jsx
+++ b/webapp/utils/channel_intro_messages.jsx
@@ -9,6 +9,7 @@ import UserProfile from 'components/user_profile.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import Constants from 'utils/constants.jsx';
import * as GlobalActions from 'action_creators/global_actions.jsx';
+import Client from 'utils/web_client.jsx';
import React from 'react';
import {FormattedMessage, FormattedHTMLMessage, FormattedDate} from 'react-intl';
@@ -40,7 +41,7 @@ export function createDMIntroMessage(channel) {
<div className='post-profile-img__container channel-intro-img'>
<img
className='post-profile-img'
- src={'/api/v1/users/' + teammate.id + '/image?time=' + teammate.update_at}
+ src={Client.getUsersRoute() + '/' + teammate.id + '/image?time=' + teammate.update_at}
height='50'
width='50'
/>
diff --git a/webapp/utils/client.jsx b/webapp/utils/client.jsx
deleted file mode 100644
index 687d47da4..000000000
--- a/webapp/utils/client.jsx
+++ /dev/null
@@ -1,1759 +0,0 @@
-// See License.txt for license information.
-
-import BrowserStore from 'stores/browser_store.jsx';
-import $ from 'jquery';
-
-import {browserHistory} from 'react-router';
-
-let translations = {
- connectionError: 'There appears to be a problem with your internet connection.',
- unknownError: 'We received an unexpected status code from the server.'
-};
-
-export function setTranslations(messages) {
- translations = messages;
-}
-
-export function track(category, action, label, property, value) {
- global.window.analytics.track(action, {category, label, property, value});
-}
-
-export function trackPage() {
- global.window.analytics.page();
-}
-
-function handleError(methodName, xhr, status, err) {
- var e = null;
- try {
- e = JSON.parse(xhr.responseText);
- } catch (parseError) {
- e = null;
- }
-
- var msg = '';
-
- if (e) {
- msg = 'method=' + methodName + ' msg=' + e.message + ' detail=' + e.detailed_error + ' rid=' + e.request_id;
- } else {
- msg = 'method=' + methodName + ' status=' + status + ' statusCode=' + xhr.status + ' err=' + err;
-
- if (xhr.status === 0) {
- e = {message: translations.connectionError};
- } else {
- e = {message: translations.unknownError + ' (' + xhr.status + ')'};
- }
- }
-
- console.error(msg); //eslint-disable-line no-console
- console.error(e); //eslint-disable-line no-console
-
- track('api', 'api_weberror', methodName, 'message', msg);
-
- if (xhr.status === 401) {
- const team = window.location.pathname.split('/')[1];
- browserHistory.push('/' + team + '/login?extra=expired&redirect=' + encodeURIComponent(window.location.pathname + window.location.search));
- }
-
- return e;
-}
-
-export function getTranslations(url, success, error) {
- $.ajax({
- url: url,
- dataType: 'json',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getTranslations', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function createTeamFromSignup(teamSignup, success, error) {
- $.ajax({
- url: '/api/v1/teams/create_from_signup',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(teamSignup),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('createTeamFromSignup', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function createTeamWithLdap(teamSignup, success, error) {
- $.ajax({
- url: '/api/v1/teams/create_with_ldap',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(teamSignup),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('createTeamFromSignup', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function createTeamWithSSO(team, service, success, error) {
- $.ajax({
- url: '/api/v1/teams/create_with_sso/' + service,
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(team),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('createTeamWithSSO', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function createUser(user, data, emailHash, success, error) {
- $.ajax({
- url: '/api/v1/users/create?d=' + encodeURIComponent(data) + '&h=' + encodeURIComponent(emailHash),
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(user),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('createUser', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_create', user.team_id, 'email', user.email);
-}
-
-export function updateUser(user, success, error) {
- $.ajax({
- url: '/api/v1/users/update',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(user),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateUser', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_update');
-}
-
-export function updatePassword(data, success, error) {
- $.ajax({
- url: '/api/v1/users/newpassword',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('newPassword', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_newpassword');
-}
-
-export function updateUserNotifyProps(data, success, error) {
- $.ajax({
- url: '/api/v1/users/update_notify',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateUserNotifyProps', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function updateRoles(data, success, error) {
- $.ajax({
- url: '/api/v1/users/update_roles',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateRoles', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_update_roles');
-}
-
-export function updateActive(userId, active, success, error) {
- var data = {};
- data.user_id = userId;
- data.active = '' + active;
-
- $.ajax({
- url: '/api/v1/users/update_active',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateActive', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_update_roles');
-}
-
-export function sendPasswordReset(data, success, error) {
- $.ajax({
- url: '/api/v1/users/send_password_reset',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('sendPasswordReset', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_send_password_reset');
-}
-
-export function resetPassword(data, success, error) {
- $.ajax({
- url: '/api/v1/users/reset_password',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('resetPassword', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_reset_password');
-}
-
-export function emailToOAuth(data, success, error) {
- $.ajax({
- url: '/api/v1/users/claim/email_to_oauth',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('emailToOAuth', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_email_to_oauth');
-}
-
-export function oauthToEmail(data, success, error) {
- $.ajax({
- url: '/api/v1/users/claim/oauth_to_email',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('oauthToEmail', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_oauth_to_email');
-}
-
-export function emailToLDAP(data, success, error) {
- $.ajax({
- url: '/api/v1/users/claim/email_to_ldap',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('emailToLDAP', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_email_to_ldap');
-}
-
-export function ldapToEmail(data, success, error) {
- $.ajax({
- url: '/api/v1/users/claim/ldap_to_email',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('ldapToEmail', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_users_ldap_to_email');
-}
-
-export function logout(success, error) {
- track('api', 'api_users_logout');
- $.ajax({
- url: '/api/v1/users/logout',
- type: 'POST',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('logout', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function checkMfa(method, team, loginId, success, error) {
- $.ajax({
- url: '/api/v1/users/mfa',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({method, team_name: team, login_id: loginId}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('checkMfa', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function loginByEmail(name, email, password, token, success, error) {
- $.ajax({
- url: '/api/v1/users/login',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({name, email, password, token}),
- success: function onSuccess(data, textStatus, xhr) {
- track('api', 'api_users_login_success', data.team_id, 'email', data.email);
- sessionStorage.removeItem(data.id + '_last_error');
- BrowserStore.signalLogin();
- success(data, textStatus, xhr);
- },
- error: function onError(xhr, status, err) {
- track('api', 'api_users_login_fail', name, 'email', email);
-
- var e = handleError('loginByEmail', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function loginByUsername(name, username, password, success, error) {
- $.ajax({
- url: '/api/v1/users/login',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({name, username, password}),
- success: function onSuccess(data, textStatus, xhr) {
- track('api', 'api_users_login_success', data.team_id, 'username', data.username);
- sessionStorage.removeItem(data.id + '_last_error');
- BrowserStore.signalLogin();
- success(data, textStatus, xhr);
- },
- error: function onError(xhr, status, err) {
- track('api', 'api_users_login_fail', name, 'username', username);
-
- var e = handleError('loginByUsername', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function loginByLdap(teamName, id, password, token, success, error) {
- $.ajax({
- url: '/api/v1/users/login_ldap',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({teamName, id, password, token}),
- success: function onSuccess(data, textStatus, xhr) {
- track('api', 'api_users_loginLdap_success', data.team_id, 'id', id);
- sessionStorage.removeItem(data.id + '_last_error');
- BrowserStore.signalLogin();
- success(data, textStatus, xhr);
- },
- error: function onError(xhr, status, err) {
- track('api', 'api_users_loginLdap_fail', teamName, 'id', id);
-
- var e = handleError('loginByLdap', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function revokeSession(altId, success, error) {
- $.ajax({
- url: '/api/v1/users/revoke_session',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({id: altId}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('revokeSession', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getSessions(userId, success, error) {
- $.ajax({
- cache: false,
- url: '/api/v1/users/' + userId + '/sessions',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getSessions', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getAudits(userId, success, error) {
- $.ajax({
- url: '/api/v1/users/' + userId + '/audits',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getAudits', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getComplianceReports(success, error) {
- $.ajax({
- url: '/api/v1/admin/compliance_reports',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getComplianceReports', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function saveComplianceReports(job, success, error) {
- $.ajax({
- url: '/api/v1/admin/save_compliance_report',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(job),
- success,
- error: (xhr, status, err) => {
- var e = handleError('saveComplianceReports', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getLogs(success, error) {
- $.ajax({
- url: '/api/v1/admin/logs',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getLogs', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getServerAudits(success, error) {
- $.ajax({
- url: '/api/v1/admin/audits',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getServerAudits', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getConfig(success, error) {
- return $.ajax({
- url: '/api/v1/admin/config',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getConfig', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getAnalytics(name, teamId, success, error) {
- let url = '/api/v1/admin/analytics/';
- if (teamId == null) {
- url += name;
- } else {
- url += teamId + '/' + name;
- }
- $.ajax({
- url,
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: (xhr, status, err) => {
- var e = handleError('getSystemAnalytics', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getClientConfig(success, error) {
- return $.ajax({
- url: '/api/v1/admin/client_props',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getClientConfig', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getTeamAnalytics(teamId, name, success, error) {
- $.ajax({
- url: '/api/v1/admin/analytics/' + teamId + '/' + name,
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: (xhr, status, err) => {
- var e = handleError('getTeamAnalytics', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function saveConfig(config, success, error) {
- $.ajax({
- url: '/api/v1/admin/save_config',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(config),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('saveConfig', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function logClientError(msg) {
- var l = {};
- l.level = 'ERROR';
- l.message = msg;
-
- $.ajax({
- url: '/api/v1/admin/log_client',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(l)
- });
-}
-
-export function testEmail(config, success, error) {
- $.ajax({
- url: '/api/v1/admin/test_email',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(config),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('testEmail', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getAllTeams(success, error) {
- $.ajax({
- url: '/api/v1/teams/all',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getAllTeams', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getMeLoggedIn(success, error) {
- return $.ajax({
- cache: false,
- url: '/api/v1/users/me_logged_in',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getMeLoggedIn', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getMe(success, error) {
- var currentUser = null;
- $.ajax({
- cache: false,
- url: '/api/v1/users/me',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success: function gotUser(data, textStatus, xhr) {
- currentUser = data;
- if (success) {
- success(data, textStatus, xhr);
- }
- },
- error: function onError(xhr, status, err) {
- if (error) {
- var e = handleError('getMe', xhr, status, err);
- error(e);
- }
- }
- });
-
- return currentUser;
-}
-
-export function inviteMembers(data, success, error) {
- $.ajax({
- url: '/api/v1/teams/invite_members',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('inviteMembers', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_teams_invite_members');
-}
-
-export function updateTeam(team, success, error) {
- $.ajax({
- url: '/api/v1/teams/update',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(team),
- success,
- error: (xhr, status, err) => {
- var e = handleError('updateTeam', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_teams_update_name');
-}
-
-export function signupTeam(email, success, error) {
- $.ajax({
- url: '/api/v1/teams/signup',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({email: email}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('singupTeam', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_teams_signup');
-}
-
-export function createTeam(team, success, error) {
- $.ajax({
- url: '/api/v1/teams/create',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(team),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('createTeam', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function findTeamByName(teamName, success, error) {
- $.ajax({
- url: '/api/v1/teams/find_team_by_name',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({name: teamName}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('findTeamByName', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function createChannel(channel, success, error) {
- $.ajax({
- url: '/api/v1/channels/create',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(channel),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('createChannel', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_create');
-}
-
-export function createDirectChannel(channel, userId, success, error) {
- $.ajax({
- url: '/api/v1/channels/create_direct',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({user_id: userId}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('createDirectChannel', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_create_direct');
-}
-
-export function updateChannel(channel, success, error) {
- $.ajax({
- url: '/api/v1/channels/update',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(channel),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateChannel', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_update');
-}
-
-export function updateChannelHeader(channelId, header, success, error) {
- const data = {
- channel_id: channelId,
- channel_header: header
- };
-
- $.ajax({
- url: '/api/v1/channels/update_header',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateChannelHeader', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_header');
-}
-
-export function updateChannelPurpose(data, success, error) {
- $.ajax({
- url: '/api/v1/channels/update_purpose',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateChannelPurpose', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_purpose');
-}
-
-export function updateNotifyProps(data, success, error) {
- $.ajax({
- url: '/api/v1/channels/update_notify_props',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateNotifyProps', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function joinChannel(id, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + id + '/join',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('joinChannel', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_join');
-}
-
-export function leaveChannel(id, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + id + '/leave',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('leaveChannel', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_leave');
-}
-
-export function deleteChannel(id, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + id + '/delete',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('deleteChannel', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_delete');
-}
-
-export function updateLastViewedAt(channelId, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + channelId + '/update_last_viewed_at',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updateLastViewedAt', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getChannels(success, error) {
- return $.ajax({
- cache: false,
- url: '/api/v1/channels/',
- dataType: 'json',
- type: 'GET',
- success,
- ifModified: true,
- error: function onError(xhr, status, err) {
- var e = handleError('getChannels', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getChannel(id, success, error) {
- $.ajax({
- cache: false,
- url: '/api/v1/channels/' + id + '/',
- dataType: 'json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getChannel', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channel_get');
-}
-
-export function getMoreChannels(success, error) {
- $.ajax({
- url: '/api/v1/channels/more',
- dataType: 'json',
- type: 'GET',
- success,
- ifModified: true,
- error: function onError(xhr, status, err) {
- var e = handleError('getMoreChannels', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getChannelCounts(success, error) {
- $.ajax({
- cache: false,
- url: '/api/v1/channels/counts',
- dataType: 'json',
- type: 'GET',
- success,
- ifModified: true,
- error: function onError(xhr, status, err) {
- var e = handleError('getChannelCounts', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getChannelExtraInfo(id, memberLimit, success, error) {
- let url = '/api/v1/channels/' + id + '/extra_info';
-
- if (memberLimit) {
- url += '/' + memberLimit;
- }
-
- return $.ajax({
- url,
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getChannelExtraInfo', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function executeCommand(channelId, command, suggest, success, error) {
- $.ajax({
- url: '/api/v1/commands/execute',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify({channelId, command, suggest: '' + suggest}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('executeCommand', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function addCommand(cmd, success, error) {
- $.ajax({
- url: '/api/v1/commands/create',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(cmd),
- success,
- error: (xhr, status, err) => {
- var e = handleError('addCommand', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function deleteCommand(data, success, error) {
- $.ajax({
- url: '/api/v1/commands/delete',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: (xhr, status, err) => {
- var e = handleError('deleteCommand', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function listTeamCommands(success, error) {
- $.ajax({
- url: '/api/v1/commands/list_team_commands',
- dataType: 'json',
- type: 'GET',
- success,
- error: (xhr, status, err) => {
- var e = handleError('listTeamCommands', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function regenCommandToken(data, success, error) {
- $.ajax({
- url: '/api/v1/commands/regen_token',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: (xhr, status, err) => {
- var e = handleError('regenCommandToken', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function listCommands(success, error) {
- $.ajax({
- url: '/api/v1/commands/list',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('listCommands', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getPostsPage(channelId, offset, limit, success, error, complete) {
- $.ajax({
- cache: false,
- url: '/api/v1/channels/' + channelId + '/posts/' + offset + '/' + limit,
- dataType: 'json',
- type: 'GET',
- ifModified: true,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getPosts', xhr, status, err);
- error(e);
- },
- complete: complete
- });
-}
-
-export function getPosts(channelId, since, success, error, complete) {
- return $.ajax({
- url: '/api/v1/channels/' + channelId + '/posts/' + since,
- dataType: 'json',
- type: 'GET',
- ifModified: true,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getPosts', xhr, status, err);
- error(e);
- },
- complete: complete
- });
-}
-
-export function getPostsBefore(channelId, post, offset, numPost, success, error, complete) {
- $.ajax({
- url: '/api/v1/channels/' + channelId + '/post/' + post + '/before/' + offset + '/' + numPost,
- dataType: 'json',
- type: 'GET',
- ifModified: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getPostsBefore', xhr, status, err);
- error(e);
- },
- complete: complete
- });
-}
-
-export function getPostsAfter(channelId, post, offset, numPost, success, error, complete) {
- $.ajax({
- url: '/api/v1/channels/' + channelId + '/post/' + post + '/after/' + offset + '/' + numPost,
- dataType: 'json',
- type: 'GET',
- ifModified: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getPostsAfter', xhr, status, err);
- error(e);
- },
- complete: complete
- });
-}
-
-export function getPost(channelId, postId, success, error, complete) {
- $.ajax({
- cache: false,
- url: '/api/v1/channels/' + channelId + '/post/' + postId,
- dataType: 'json',
- type: 'GET',
- ifModified: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getPost', xhr, status, err);
- error(e);
- },
- complete
- });
-}
-
-export function getPostById(postId, success, error, complete) {
- $.ajax({
- cache: false,
- url: '/api/v1/posts/' + postId,
- dataType: 'json',
- type: 'GET',
- ifModified: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getPostById', xhr, status, err);
- error(e);
- },
- complete
- });
-}
-
-export function search(terms, success, error) {
- $.ajax({
- url: '/api/v1/posts/search',
- dataType: 'json',
- type: 'GET',
- data: {terms: terms},
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('search', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_posts_search');
-}
-
-export function deletePost(channelId, id, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + channelId + '/post/' + id + '/delete',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('deletePost', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_posts_delete');
-}
-
-export function createPost(post, channel, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + post.channel_id + '/create',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(post),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('createPost', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_posts_create', channel.name, 'length', post.message.length);
-
- // global.window.analytics.track('api_posts_create', {
- // category: 'api',
- // channel_name: channel.name,
- // channel_type: channel.type,
- // length: post.message.length,
- // files: (post.filenames || []).length,
- // mentions: (post.message.match('/<mention>/g') || []).length
- // });
-}
-
-export function updatePost(post, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + post.channel_id + '/update',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(post),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('updatePost', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_posts_update');
-}
-
-export function addChannelMember(id, data, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + id + '/add',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('addChannelMember', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_add_member');
-}
-
-export function removeChannelMember(id, data, success, error) {
- $.ajax({
- url: '/api/v1/channels/' + id + '/remove',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('removeChannelMember', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_channels_remove_member');
-}
-
-export function getProfiles(success, error) {
- $.ajax({
- cache: false,
- url: '/api/v1/users/profiles',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- ifModified: true,
- error: function onError(xhr, status, err) {
- var e = handleError('getProfiles', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getProfilesForTeam(teamId, success, error) {
- $.ajax({
- cache: false,
- url: '/api/v1/users/profiles/' + teamId,
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getProfilesForTeam', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function uploadFile(formData, success, error) {
- var request = $.ajax({
- url: '/api/v1/files/upload',
- type: 'POST',
- data: formData,
- cache: false,
- contentType: false,
- processData: false,
- success,
- error: function onError(xhr, status, err) {
- if (err !== 'abort') {
- var e = handleError('uploadFile', xhr, status, err);
- error(e);
- }
- }
- });
-
- track('api', 'api_files_upload');
-
- return request;
-}
-
-export function getFileInfo(filename, success, error) {
- $.ajax({
- url: '/api/v1/files/get_info' + filename,
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success: (data) => {
- success(data);
- },
- error: function onError(xhr, status, err) {
- var e = handleError('getFileInfo', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getPublicLink(data, success, error) {
- $.ajax({
- url: '/api/v1/files/get_public_link',
- dataType: 'json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getPublicLink', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function uploadProfileImage(imageData, success, error) {
- $.ajax({
- url: '/api/v1/users/newimage',
- type: 'POST',
- data: imageData,
- cache: false,
- contentType: false,
- processData: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('uploadProfileImage', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function importSlack(fileData, success, error) {
- $.ajax({
- url: '/api/v1/teams/import_team',
- type: 'POST',
- data: fileData,
- cache: false,
- contentType: false,
- processData: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('importTeam', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function exportTeam(success, error) {
- $.ajax({
- url: '/api/v1/teams/export_team',
- type: 'GET',
- dataType: 'json',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('exportTeam', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getStatuses(ids, success, error) {
- $.ajax({
- url: '/api/v1/users/status',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(ids),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getStatuses', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getMyTeam(success, error) {
- return $.ajax({
- url: '/api/v1/teams/me',
- dataType: 'json',
- type: 'GET',
- success,
- ifModified: true,
- error: function onError(xhr, status, err) {
- var e = handleError('getMyTeam', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function registerOAuthApp(app, success, error) {
- $.ajax({
- url: '/api/v1/oauth/register',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(app),
- success: success,
- error: (xhr, status, err) => {
- const e = handleError('registerApp', xhr, status, err);
- error(e);
- }
- });
-
- module.exports.track('api', 'api_apps_register');
-}
-
-export function allowOAuth2(responseType, clientId, redirectUri, state, scope, success, error) {
- $.ajax({
- url: '/api/v1/oauth/allow?response_type=' + responseType + '&client_id=' + clientId + '&redirect_uri=' + redirectUri + '&scope=' + scope + '&state=' + state,
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: (xhr, status, err) => {
- const e = handleError('allowOAuth2', xhr, status, err);
- error(e);
- }
- });
-
- module.exports.track('api', 'api_users_allow_oauth2');
-}
-
-export function addIncomingHook(hook, success, error) {
- $.ajax({
- url: '/api/v1/hooks/incoming/create',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(hook),
- success,
- error: (xhr, status, err) => {
- var e = handleError('addIncomingHook', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function deleteIncomingHook(data, success, error) {
- $.ajax({
- url: '/api/v1/hooks/incoming/delete',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: (xhr, status, err) => {
- var e = handleError('deleteIncomingHook', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function listIncomingHooks(success, error) {
- $.ajax({
- url: '/api/v1/hooks/incoming/list',
- dataType: 'json',
- type: 'GET',
- success,
- error: (xhr, status, err) => {
- var e = handleError('listIncomingHooks', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getAllPreferences(success, error) {
- return $.ajax({
- url: '/api/v1/preferences/',
- dataType: 'json',
- type: 'GET',
- success,
- error: (xhr, status, err) => {
- var e = handleError('getAllPreferences', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getPreferenceCategory(category, success, error) {
- $.ajax({
- url: `/api/v1/preferences/${category}`,
- dataType: 'json',
- type: 'GET',
- success,
- error: (xhr, status, err) => {
- var e = handleError('getPreferenceCategory', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function savePreferences(preferences, success, error) {
- $.ajax({
- url: '/api/v1/preferences/save',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(preferences),
- success,
- error: (xhr, status, err) => {
- var e = handleError('savePreferences', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function addOutgoingHook(hook, success, error) {
- $.ajax({
- url: '/api/v1/hooks/outgoing/create',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(hook),
- success,
- error: (xhr, status, err) => {
- var e = handleError('addOutgoingHook', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function deleteOutgoingHook(data, success, error) {
- $.ajax({
- url: '/api/v1/hooks/outgoing/delete',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: (xhr, status, err) => {
- var e = handleError('deleteOutgoingHook', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function listOutgoingHooks(success, error) {
- $.ajax({
- url: '/api/v1/hooks/outgoing/list',
- dataType: 'json',
- type: 'GET',
- success,
- error: (xhr, status, err) => {
- var e = handleError('listOutgoingHooks', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function regenOutgoingHookToken(data, success, error) {
- $.ajax({
- url: '/api/v1/hooks/outgoing/regen_token',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: (xhr, status, err) => {
- var e = handleError('regenOutgoingHookToken', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function uploadLicenseFile(formData, success, error) {
- $.ajax({
- url: '/api/v1/license/add',
- type: 'POST',
- data: formData,
- cache: false,
- contentType: false,
- processData: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('uploadLicenseFile', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_license_upload');
-}
-
-export function removeLicenseFile(success, error) {
- $.ajax({
- url: '/api/v1/license/remove',
- type: 'POST',
- cache: false,
- contentType: false,
- processData: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('removeLicenseFile', xhr, status, err);
- error(e);
- }
- });
-
- track('api', 'api_license_upload');
-}
-
-export function getClientLicenceConfig(success, error) {
- return $.ajax({
- url: '/api/v1/license/client_config',
- dataType: 'json',
- contentType: 'application/json',
- type: 'GET',
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getClientLicenceConfig', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function getInviteInfo(success, error, id) {
- $.ajax({
- url: '/api/v1/teams/get_invite_info',
- type: 'POST',
- dataType: 'json',
- contentType: 'application/json',
- data: JSON.stringify({invite_id: id}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('getInviteInfo', xhr, status, err);
- if (error) {
- error(e);
- }
- }
- });
-}
-
-export function verifyEmail(success, error, uid, hid) {
- $.ajax({
- url: '/api/v1/users/verify_email',
- type: 'POST',
- contentType: 'application/json',
- dataType: 'text',
- data: JSON.stringify({uid, hid}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('verifyEmail', xhr, status, err);
- if (error) {
- error(e);
- }
- }
- });
-}
-
-export function resendVerification(success, error, teamName, email) {
- $.ajax({
- url: '/api/v1/users/resend_verification',
- type: 'POST',
- contentType: 'application/json',
- dataType: 'text',
- data: JSON.stringify({team_name: teamName, email}),
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('resendVerification', xhr, status, err);
- if (error) {
- error(e);
- }
- }
- });
-}
-
-export function updateMfa(data, success, error) {
- $.ajax({
- url: '/api/v1/users/update_mfa',
- dataType: 'json',
- contentType: 'application/json',
- type: 'POST',
- data: JSON.stringify(data),
- success,
- error: (xhr, status, err) => {
- var e = handleError('updateMfa', xhr, status, err);
- error(e);
- }
- });
-}
-
-export function uploadBrandImage(image, success, error) {
- const formData = new FormData();
- formData.append('image', image, image.name);
-
- $.ajax({
- url: '/api/v1/admin/upload_brand_image',
- type: 'POST',
- data: formData,
- cache: false,
- contentType: false,
- processData: false,
- success,
- error: function onError(xhr, status, err) {
- var e = handleError('uploadBrandImage', xhr, status, err);
- error(e);
- }
- });
-}
diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx
index 87f4153fb..9bdf348cd 100644
--- a/webapp/utils/constants.jsx
+++ b/webapp/utils/constants.jsx
@@ -61,6 +61,7 @@ export default {
RECEIVED_ADD_MENTION: null,
RECEIVED_PROFILES: null,
+ RECEIVED_DIRECT_PROFILES: null,
RECEIVED_ME: null,
RECEIVED_SESSIONS: null,
RECEIVED_AUDITS: null,
@@ -92,6 +93,9 @@ export default {
RECEIVED_SERVER_AUDITS: null,
RECEIVED_SERVER_COMPLIANCE_REPORTS: null,
RECEIVED_ALL_TEAMS: null,
+ RECEIVED_ALL_TEAM_LISTINGS: null,
+ RECEIVED_TEAM_MEMBERS: null,
+ RECEIVED_MEMBERS_FOR_TEAM: null,
RECEIVED_LOCALE: null,
@@ -205,6 +209,7 @@ export default {
LDAP_SERVICE: 'ldap',
USERNAME_SERVICE: 'username',
SIGNIN_CHANGE: 'signin_change',
+ PASSWORD_CHANGE: 'password_change',
SIGNIN_VERIFIED: 'verified',
SESSION_EXPIRED: 'expired',
POST_CHUNK_SIZE: 60,
diff --git a/webapp/utils/utils.jsx b/webapp/utils/utils.jsx
index c4f4a025e..a1e16928b 100644
--- a/webapp/utils/utils.jsx
+++ b/webapp/utils/utils.jsx
@@ -10,9 +10,8 @@ import PreferenceStore from 'stores/preference_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import Constants from 'utils/constants.jsx';
var ActionTypes = Constants.ActionTypes;
-import * as Client from './client.jsx';
import * as AsyncClient from './async_client.jsx';
-import * as client from './client.jsx';
+import Client from './web_client.jsx';
import React from 'react';
import {browserHistory} from 'react-router';
@@ -1114,7 +1113,7 @@ export function fileSizeToString(bytes) {
// Converts a filename (like those attached to Post objects) to a url that can be used to retrieve attachments from the server.
export function getFileUrl(filename) {
- return getWindowLocationOrigin() + '/api/v1/files/get' + filename;
+ return getWindowLocationOrigin() + Client.getFilesRoute() + '/get' + filename;
}
// Gets the name of a file (including extension) from a given url or file path.
@@ -1210,13 +1209,17 @@ export function importSlack(file, success, error) {
formData.append('filesize', file.size);
formData.append('importFrom', 'slack');
- client.importSlack(formData, success, error);
+ Client.importSlack(formData, success, error);
}
export function getTeamURLFromAddressBar() {
return window.location.origin + '/' + window.location.pathname.split('/')[1];
}
+export function getTeamNameFromUrl() {
+ return window.location.pathname.split('/')[1];
+}
+
export function getTeamURLNoOriginFromAddressBar() {
return '/' + window.location.pathname.split('/')[1];
}
@@ -1263,16 +1266,11 @@ export function openDirectChannelToUser(user, successCb, errorCb) {
};
Client.createDirectChannel(
- channel,
user.id,
(data) => {
Client.getChannel(
data.id,
- (data2, textStatus, xhr) => {
- if (xhr.status === 304 || !data2) {
- return;
- }
-
+ (data2) => {
AppDispatcher.handleServerAction({
type: ActionTypes.RECEIVED_CHANNEL,
channel: data2.channel,
@@ -1400,7 +1398,7 @@ export function localizeMessage(id, defaultMessage) {
}
export function getProfilePicSrcForPost(post, timestamp) {
- let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp;
+ let src = Client.getUsersRoute() + '/' + post.user_id + '/image?time=' + timestamp;
if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
if (post.props.override_icon_url) {
src = post.props.override_icon_url;
diff --git a/webapp/utils/web_client.jsx b/webapp/utils/web_client.jsx
new file mode 100644
index 000000000..6071b4bb4
--- /dev/null
+++ b/webapp/utils/web_client.jsx
@@ -0,0 +1,67 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import Client from '../client/client.jsx';
+import TeamStore from '../stores/team_store.jsx';
+import BrowserStore from '../stores/browser_store.jsx';
+import * as GlobalActions from 'action_creators/global_actions.jsx';
+
+const HTTP_UNAUTHORIZED = 401;
+
+class WebClientClass extends Client {
+ constructor() {
+ super();
+ this.enableLogErrorsToConsole(true);
+ TeamStore.addChangeListener(this.onTeamStoreChanged);
+ }
+
+ onTeamStoreChanged = () => {
+ this.setTeamId(TeamStore.getCurrentId());
+ }
+
+ track = (category, action, label, property, value) => {
+ if (global.window && global.window.analytics) {
+ global.window.analytics.track(action, {category, label, property, value});
+ }
+ }
+
+ trackPage = () => {
+ if (global.window && global.window.analytics) {
+ global.window.analytics.page();
+ }
+ }
+
+ handleError = (err, res) => { // eslint-disable-line no-unused-vars
+ if (err.status === HTTP_UNAUTHORIZED) {
+ GlobalActions.emitUserLoggedOutEvent('/login');
+ }
+ }
+
+ // not sure why but super.login doesn't work if using an () => arrow functions.
+ // I think this might be a webpack issue.
+ webLogin(email, username, password, token, success, error) {
+ this.login(
+ email,
+ username,
+ password,
+ token,
+ (data) => {
+ this.track('api', 'api_users_login_success', '', 'email', data.email);
+ BrowserStore.signalLogin();
+
+ if (success) {
+ success(data);
+ }
+ },
+ (err) => {
+ this.track('api', 'api_users_login_fail', name, 'email', email);
+ if (error) {
+ error(err);
+ }
+ }
+ );
+ }
+}
+
+var WebClient = new WebClientClass();
+export default WebClient;
diff --git a/webapp/webpack.config-test.js b/webapp/webpack.config-test.js
new file mode 100644
index 000000000..aaeefeb8c
--- /dev/null
+++ b/webapp/webpack.config-test.js
@@ -0,0 +1,131 @@
+const webpack = require('webpack');
+const path = require('path');
+const ExtractTextPlugin = require('extract-text-webpack-plugin');
+const CopyWebpackPlugin = require('copy-webpack-plugin');
+const nodeExternals = require('webpack-node-externals');
+
+const htmlExtract = new ExtractTextPlugin('html', 'root.html');
+
+const NPM_TARGET = process.env.npm_lifecycle_event; //eslint-disable-line no-process-env
+
+var DEV = true;
+var FULLMAP = false;
+if (NPM_TARGET === 'run' || NPM_TARGET === 'run-fullmap') {
+ DEV = true;
+ if (NPM_TARGET === 'run-fullmap') {
+ FULLMAP = true;
+ }
+}
+
+var config = {
+ target: 'node',
+ externals: [nodeExternals()],
+ module: {
+ loaders: [
+ {
+ test: /\.jsx?$/,
+ loader: 'babel',
+ exclude: /(node_modules|non_npm_dependencies)/,
+ query: {
+ presets: ['react', 'es2015-webpack', 'stage-0'],
+ plugins: ['transform-runtime'],
+ cacheDirectory: DEV
+ }
+ },
+ {
+ test: /\.json$/,
+ loader: 'json'
+ },
+ {
+ test: /(node_modules|non_npm_dependencies)\/.+\.(js|jsx)$/,
+ loader: 'imports',
+ query: {
+ $: 'jquery',
+ jQuery: 'jquery'
+ }
+ },
+ {
+ test: /\.scss$/,
+ loaders: ['style', 'css', 'sass']
+ },
+ {
+ test: /\.css$/,
+ loaders: ['style', 'css']
+ },
+ {
+ test: /\.(png|eot|tiff|svg|woff2|woff|ttf|gif|mp3|jpg)$/,
+ loader: 'file',
+ query: {
+ name: 'files/[hash].[ext]'
+ }
+ },
+ {
+ test: /\.html$/,
+ loader: htmlExtract.extract('html?attrs=link:href')
+ }
+ ]
+ },
+ sassLoader: {
+ includePaths: ['node_modules/compass-mixins/lib']
+ },
+ plugins: [
+ new webpack.ProvidePlugin({
+ 'window.jQuery': 'jquery'
+ }),
+ htmlExtract,
+ new CopyWebpackPlugin([
+ {from: 'images/emoji', to: 'emoji'}
+ ]),
+ new webpack.LoaderOptionsPlugin({
+ minimize: !DEV,
+ debug: false
+ })
+ ],
+ resolve: {
+ alias: {
+ jquery: 'jquery/dist/jquery'
+ },
+ modules: [
+ 'node_modules',
+ 'non_npm_dependencies',
+ path.resolve(__dirname)
+ ]
+ }
+};
+
+// Development mode configuration
+if (DEV) {
+ if (FULLMAP) {
+ config.devtool = 'source-map';
+ } else {
+ config.devtool = 'eval-cheap-module-source-map';
+ }
+}
+
+// Production mode configuration
+if (!DEV) {
+ config.devtool = 'source-map';
+ config.plugins.push(
+ new webpack.optimize.UglifyJsPlugin({
+ 'screw-ie8': true,
+ mangle: {
+ toplevel: false
+ },
+ compress: {
+ warnings: false
+ },
+ comments: false
+ })
+ );
+ config.plugins.push(
+ new webpack.optimize.AggressiveMergingPlugin()
+ );
+ config.plugins.push(
+ new webpack.optimize.OccurrenceOrderPlugin(true)
+ );
+ config.plugins.push(
+ new webpack.optimize.DedupePlugin()
+ );
+}
+
+module.exports = config;
diff --git a/webapp/webpack.config.js b/webapp/webpack.config.js
index 4e2d6b70d..dac074c0b 100644
--- a/webapp/webpack.config.js
+++ b/webapp/webpack.config.js
@@ -88,7 +88,8 @@ var config = {
],
resolve: {
alias: {
- jquery: 'jquery/dist/jquery'
+ jquery: 'jquery/dist/jquery',
+ superagent: 'node_modules/superagent/lib/client'
},
modules: [
'node_modules',