From b8c80c6d9520ec0af0407c576ee565df85fcc41f Mon Sep 17 00:00:00 2001 From: Florian Orben Date: Wed, 14 Oct 2015 20:21:25 +0200 Subject: Don't auto-add http:// to mailto: and ftp: markdown links --- web/react/utils/markdown.jsx | 2 +- web/react/utils/text_formatting.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'web') diff --git a/web/react/utils/markdown.jsx b/web/react/utils/markdown.jsx index 12d6dd424..2813798d2 100644 --- a/web/react/utils/markdown.jsx +++ b/web/react/utils/markdown.jsx @@ -32,7 +32,7 @@ export class MattermostMarkdownRenderer extends marked.Renderer { link(href, title, text) { let outHref = href; - if (outHref.lastIndexOf('http', 0) !== 0) { + if (!(/^(mailto|https?|ftp)/.test(outHref))) { outHref = `http://${outHref}`; } diff --git a/web/react/utils/text_formatting.jsx b/web/react/utils/text_formatting.jsx index 2b6e6e14e..d79aeed68 100644 --- a/web/react/utils/text_formatting.jsx +++ b/web/react/utils/text_formatting.jsx @@ -89,7 +89,7 @@ function autolinkUrls(text, tokens) { if (match.getType() === 'email') { url = `mailto:${url}`; - } else if (url.lastIndexOf('http', 0) !== 0) { + } else if (!(/^(mailto|https?|ftp)/.test(url))) { url = `http://${url}`; } -- cgit v1.2.3-1-g7c22 From f0a3c5983492130adb7bf6b92d2682b029c302e9 Mon Sep 17 00:00:00 2001 From: Florian Orben Date: Wed, 14 Oct 2015 21:41:03 +0200 Subject: PLT-42: Add "File uploading" text left of "Add Comment" when file is uploading --- web/react/components/create_comment.jsx | 12 ++++++++++++ web/sass-files/sass/partials/_post_right.scss | 5 +++++ 2 files changed, 17 insertions(+) (limited to 'web') diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx index 680d693f1..2ac5d2179 100644 --- a/web/react/components/create_comment.jsx +++ b/web/react/components/create_comment.jsx @@ -255,6 +255,17 @@ export default class CreateComment extends React.Component { postFooterClassName += ' has-error'; } + let uploadsInProgressText = null; + if (this.state.uploadsInProgress.length > 0) { + uploadsInProgressText = ( + + {this.state.uploadsInProgress.length === 1 ? 'File uploading' : 'Files uploading'} + + ); + } + return (
@@ -295,6 +306,7 @@ export default class CreateComment extends React.Component { value='Add Comment' onClick={this.handleSubmit} /> + {uploadsInProgressText} {postError} {serverError}
diff --git a/web/sass-files/sass/partials/_post_right.scss b/web/sass-files/sass/partials/_post_right.scss index e4860b286..b72176a11 100644 --- a/web/sass-files/sass/partials/_post_right.scss +++ b/web/sass-files/sass/partials/_post_right.scss @@ -36,6 +36,11 @@ .post-create-footer { padding-top: 10px; } + .post-right-comments-upload-in-progress { + padding: 6px 0; + color: #a8adb7; + margin-right: 10px; + } } } -- cgit v1.2.3-1-g7c22 From 536c55dc5f9f80ea67e2ccb39c1b4a276fbddc07 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Wed, 7 Oct 2015 16:58:27 -0700 Subject: Initial changes to audit log along with structure for handling different audit types --- web/react/components/access_history_modal.jsx | 91 ++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 16 deletions(-) (limited to 'web') diff --git a/web/react/components/access_history_modal.jsx b/web/react/components/access_history_modal.jsx index 2ad4d5b00..9aeb7f682 100644 --- a/web/react/components/access_history_modal.jsx +++ b/web/react/components/access_history_modal.jsx @@ -15,7 +15,7 @@ export default class AccessHistoryModal extends React.Component { this.onHide = this.onHide.bind(this); this.onShow = this.onShow.bind(this); - let state = this.getStateFromStoresForAudits(); + const state = this.getStateFromStoresForAudits(); state.moreInfo = []; this.state = state; @@ -54,29 +54,91 @@ export default class AccessHistoryModal extends React.Component { } render() { var accessList = []; - var currentHistoryDate = null; for (var i = 0; i < this.state.audits.length; i++) { var currentAudit = this.state.audits[i]; - var newHistoryDate = new Date(currentAudit.create_at); - var newDate = null; - - if (!currentHistoryDate || currentHistoryDate.toLocaleDateString() !== newHistoryDate.toLocaleDateString()) { - currentHistoryDate = newHistoryDate; - newDate = (
{currentHistoryDate.toDateString()}
); - } + var currentDate = new Date(currentAudit.create_at); if (!currentAudit.session_id && currentAudit.action.search('/users/login') !== -1) { currentAudit.session_id = 'N/A (Login attempt)'; } + const currentActionURL = currentAudit.action.replace(/\/api\/v[1-9]/, ''); + + let currentAuditDesc = ''; + + /* Handle specific audit type formatting semi-individually and + fall back to a best guess case if none exists + + Supported audits: + /channels + - Create Channel X + - Update Channel X + - Update Channel Description X + - Delete Channel X + - Add User to Channel X + - Remove User from Channel X + + /oauth + - Register X + - Allow Attempt/Success/Failure X + + /team + - Revoke All Sessions X (NO CORRESPONDING ADDRESS) + + - Update (users - ?) X + - Update Notify (?) X + - Login Attempt X + - Login (success/failure) X + - Logout (/logout) X + */ + switch (currentActionURL) { + case '/channels/create': + break; + case '/channels/update': + break; + case '/channels/update_desc': + break; + case /\/channels\/[A-Za-z0-9]+\/delete/: + break; + case /\/channels\/[A-Za-z0-9]+\/add/: + break; + case /\/channels\/[A-Za-z0-9]+\/remove/: + break; + case '/oauth/register': + break; + case '/oauth/allow': + break; + case '/team/': + break; + default: + let currentActionDesc = ''; + + if (currentActionURL && currentActionURL.lastIndexOf('/') !== -1) { + currentActionDesc = currentActionURL.substring(currentActionURL.lastIndexOf('/') + 1).replace('_', ' '); + currentActionDesc = Utils.toTitleCase(currentActionDesc); + } + + let currentExtraInfoDesc = ''; + if (currentAudit.extra_info) { + currentExtraInfoDesc = currentAudit.extra_info; + + if (currentExtraInfoDesc.indexOf('=') !== -1) { + currentExtraInfoDesc = currentExtraInfoDesc.substring(currentExtraInfoDesc.indexOf('=') + 1); + } + } + + currentAuditDesc = currentActionDesc + ' ' + currentExtraInfoDesc; + break; + } + var moreInfo = ( - More info + {'More info'} ); @@ -84,7 +146,6 @@ export default class AccessHistoryModal extends React.Component { moreInfo = (
{'Session ID: ' + currentAudit.session_id}
-
{'URL: ' + currentAudit.action.replace(/\/api\/v[1-9]/, '')}
); } @@ -99,11 +160,9 @@ export default class AccessHistoryModal extends React.Component { key={'accessHistoryEntryKey' + i} className='access-history__table' > -
{newDate}
-
{newHistoryDate.toLocaleTimeString(navigator.language, {hour: '2-digit', minute: '2-digit'})}
+
{currentDate.toDateString() + ' - ' + currentDate.toLocaleTimeString(navigator.language, {hour: '2-digit', minute: '2-digit'}) + ' | ' + currentAuditDesc + ' from ' + currentAudit.ip_address}
-
{'IP: ' + currentAudit.ip_address}
{moreInfo}
{divider} @@ -138,13 +197,13 @@ export default class AccessHistoryModal extends React.Component { data-dismiss='modal' aria-label='Close' > - +

- Access History + {'Access History'}

Date: Fri, 9 Oct 2015 16:35:59 -0700 Subject: Finished structure for formatting audits and began writing individual formatting for each audit --- web/react/components/access_history_modal.jsx | 193 ++++++++++++++++++-------- 1 file changed, 132 insertions(+), 61 deletions(-) (limited to 'web') diff --git a/web/react/components/access_history_modal.jsx b/web/react/components/access_history_modal.jsx index 9aeb7f682..41a865df4 100644 --- a/web/react/components/access_history_modal.jsx +++ b/web/react/components/access_history_modal.jsx @@ -14,6 +14,7 @@ export default class AccessHistoryModal extends React.Component { this.handleMoreInfo = this.handleMoreInfo.bind(this); this.onHide = this.onHide.bind(this); this.onShow = this.onShow.bind(this); + this.formatAuditInfo = this.formatAuditInfo.bind(this); const state = this.getStateFromStoresForAudits(); state.moreInfo = []; @@ -52,74 +53,127 @@ export default class AccessHistoryModal extends React.Component { newMoreInfo[index] = true; this.setState({moreInfo: newMoreInfo}); } - render() { - var accessList = []; + formatAuditInfo(currentAudit) { + const currentActionURL = currentAudit.action.replace(/\/api\/v[1-9]/, ''); - for (var i = 0; i < this.state.audits.length; i++) { - var currentAudit = this.state.audits[i]; - var currentDate = new Date(currentAudit.create_at); + let currentAuditDesc = ' '; + + /* Handle audit formatting semi-individually for each type and + fall back to a best guess case if none exists + + Supported audits: + /channels + - Create Channel X + - Update Channel X + - Update Channel Description X + - Delete Channel X + - Add User to Channel X + - Remove User from Channel X + + /oauth + - Register X + - Allow Attempt/Success/Failure X + + /team + - Revoke All Sessions X (NO CORRESPONDING ADDRESS/FUNCTION) + + - Revoke Session X + - Update (users - ?) X + - Update Notify (?) X + - Login Attempt X + - Login (success/failure) X + - Logout (/logout) X + - Verify Email (/verify_email) X + */ + switch (currentActionURL) { + + /* BREAK UP CHANNEL INTO OWN SWITCH STATEMENT TO REUSE VARIABLES AND BE CLEAN */ + case '/channels/create': + const createChannelInfo = currentAudit.extra_info.split('='); + let channelName = ''; + + if (createChannelInfo[0] === 'name') { + channelName = createChannelInfo[1]; + } + + currentAuditDesc = 'Created a new channel/group named ' + channelName; + + break; + case '/channels/update': + const updateChannelInfo = currentAudit.extra_info.split('='); + let originalChannelName = ''; + + if (updateChannelInfo[0] === 'name') { + originalChannelName = updateChannelInfo[1]; + } + + currentAuditDesc = 'Updated the channel/group name for ' + originalChannelName; + break; - if (!currentAudit.session_id && currentAudit.action.search('/users/login') !== -1) { - currentAudit.session_id = 'N/A (Login attempt)'; + /* case '/channels/update_desc': + const updateChannelInfo = currentAudit.extra_info.split('='); + let originalChannelName = ''; + + if (updateChannelInfo[0] === 'name') { + originalChannelName = updateChannelInfo[1]; } - const currentActionURL = currentAudit.action.replace(/\/api\/v[1-9]/, ''); - - let currentAuditDesc = ''; - - /* Handle specific audit type formatting semi-individually and - fall back to a best guess case if none exists - - Supported audits: - /channels - - Create Channel X - - Update Channel X - - Update Channel Description X - - Delete Channel X - - Add User to Channel X - - Remove User from Channel X - - /oauth - - Register X - - Allow Attempt/Success/Failure X - - /team - - Revoke All Sessions X (NO CORRESPONDING ADDRESS) - - - Update (users - ?) X - - Update Notify (?) X - - Login Attempt X - - Login (success/failure) X - - Logout (/logout) X - */ - switch (currentActionURL) { - case '/channels/create': - break; - case '/channels/update': - break; - case '/channels/update_desc': - break; - case /\/channels\/[A-Za-z0-9]+\/delete/: - break; - case /\/channels\/[A-Za-z0-9]+\/add/: - break; - case /\/channels\/[A-Za-z0-9]+\/remove/: - break; - case '/oauth/register': - break; - case '/oauth/allow': - break; - case '/team/': - break; - default: - let currentActionDesc = ''; + currentAuditDesc = 'Updated the channel/group name for ' + originalChannelName; + break;*/ + case /\/channels\/[A-Za-z0-9]+\/delete/: + break; + case /\/channels\/[A-Za-z0-9]+\/add/: + break; + case /\/channels\/[A-Za-z0-9]+\/remove/: + break; + case '/oauth/register': + break; + case '/oauth/allow': + break; + case '/users/login': + break; + case '/users/revoke_session': + break; + case '/users/newimage': + break; + case '/users/update': + break; + case '/users/newpassword': + break; + case '/users/update_roles': + break; + case '/users/update_active': + break; + case '/users/send_password_reset': + break; + case '/users/reset_password': + break; + case '/users/update_notify': + break; + case '/logout': + break; + case '/hooks/incoming/create': + break; + case '/hooks/incoming/delete': + break; + case '/verify_email': + break; + case '/oauth/access_token': + break; + case '': + break; + default: + if (currentAudit.extra_info.indexOf('revoked_all=') >= 0) { + // do stuff + } else { + let currentActionDesc = ' '; if (currentActionURL && currentActionURL.lastIndexOf('/') !== -1) { currentActionDesc = currentActionURL.substring(currentActionURL.lastIndexOf('/') + 1).replace('_', ' '); currentActionDesc = Utils.toTitleCase(currentActionDesc); } - let currentExtraInfoDesc = ''; + let currentExtraInfoDesc = ' '; if (currentAudit.extra_info) { currentExtraInfoDesc = currentAudit.extra_info; @@ -127,11 +181,23 @@ export default class AccessHistoryModal extends React.Component { currentExtraInfoDesc = currentExtraInfoDesc.substring(currentExtraInfoDesc.indexOf('=') + 1); } } - currentAuditDesc = currentActionDesc + ' ' + currentExtraInfoDesc; - break; } + break; + } + + const currentDate = new Date(currentAudit.create_at); + const currentAuditInfo = currentDate.toDateString() + ' - ' + currentDate.toLocaleTimeString(navigator.language, {hour: '2-digit', minute: '2-digit'}) + ' | ' + currentAuditDesc; + return currentAuditInfo; + } + render() { + var accessList = []; + + for (var i = 0; i < this.state.audits.length; i++) { + const currentAudit = this.state.audits[i]; + const currentAuditInfo = this.formatAuditInfo(currentAudit); + var moreInfo = ( +
{'IP: ' + currentAudit.ip_address}
{'Session ID: ' + currentAudit.session_id}
); @@ -161,7 +232,7 @@ export default class AccessHistoryModal extends React.Component { className='access-history__table' >
-
{currentDate.toDateString() + ' - ' + currentDate.toLocaleTimeString(navigator.language, {hour: '2-digit', minute: '2-digit'}) + ' | ' + currentAuditDesc + ' from ' + currentAudit.ip_address}
+
{currentAuditInfo}
{moreInfo}
-- cgit v1.2.3-1-g7c22 From 63b0ca8e4b63231e7b3d7cdb8b2e35d70d328114 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Mon, 12 Oct 2015 17:00:27 -0700 Subject: Added more supported audits to audit log --- web/react/components/access_history_modal.jsx | 213 +++++++++++++++++--------- 1 file changed, 140 insertions(+), 73 deletions(-) (limited to 'web') diff --git a/web/react/components/access_history_modal.jsx b/web/react/components/access_history_modal.jsx index 41a865df4..cb50ee4e7 100644 --- a/web/react/components/access_history_modal.jsx +++ b/web/react/components/access_history_modal.jsx @@ -2,6 +2,7 @@ // See License.txt for license information. var UserStore = require('../stores/user_store.jsx'); +var ChannelStore = require('../stores/channel_store.jsx'); var AsyncClient = require('../utils/async_client.jsx'); var LoadingScreen = require('./loading_screen.jsx'); var Utils = require('../utils/utils.jsx'); @@ -56,16 +57,17 @@ export default class AccessHistoryModal extends React.Component { formatAuditInfo(currentAudit) { const currentActionURL = currentAudit.action.replace(/\/api\/v[1-9]/, ''); - let currentAuditDesc = ' '; + let currentAuditDesc = ''; /* Handle audit formatting semi-individually for each type and fall back to a best guess case if none exists Supported audits: /channels - - Create Channel X - - Update Channel X - - Update Channel Description X + - Create Channel + - Create Direct Channel + - Update Channel + - Update Channel Description - Delete Channel X - Add User to Channel X - Remove User from Channel X @@ -85,84 +87,147 @@ export default class AccessHistoryModal extends React.Component { - Logout (/logout) X - Verify Email (/verify_email) X */ - switch (currentActionURL) { + if (currentActionURL.indexOf('/channels') === 0) { + const channelInfo = currentAudit.extra_info.split(' '); + const channelNameField = channelInfo[0].split('='); - /* BREAK UP CHANNEL INTO OWN SWITCH STATEMENT TO REUSE VARIABLES AND BE CLEAN */ - case '/channels/create': - const createChannelInfo = currentAudit.extra_info.split('='); + let channelURL = ''; + let channelObj; let channelName = ''; - - if (createChannelInfo[0] === 'name') { - channelName = createChannelInfo[1]; + if (channelNameField.indexOf('name') >= 0) { + channelURL = channelNameField[channelNameField.indexOf('name') + 1]; + channelObj = ChannelStore.getByName(channelURL); + if (channelObj) { + channelName = channelObj.display_name; + } else { + channelName = channelURL; + } } - currentAuditDesc = 'Created a new channel/group named ' + channelName; + switch (currentActionURL) { + case '/channels/create': + currentAuditDesc = 'Created the ' + channelName + ' channel/group'; + break; + case '/channels/create_direct': + currentAuditDesc = 'Established a direct message channel with ' + Utils.getDirectTeammate(channelObj.id).username; + break; + case '/channels/update': + currentAuditDesc = 'Updated the ' + channelName + ' channel/group name'; + break; + case '/channels/update_desc': + currentAuditDesc = 'Updated the ' + channelName + ' channel/group description'; + break; + default: + let userIdField = []; + let userId = ''; + let username = ''; + + if (channelInfo[1]) { + userIdField = channelInfo[1].split('='); + + if (userIdField.indexOf('user_id') >= 0) { + userId = userIdField[userIdField.indexOf('user_id') + 1]; + username = UserStore.getProfile(userId).username; + } + } - break; - case '/channels/update': - const updateChannelInfo = currentAudit.extra_info.split('='); - let originalChannelName = ''; + if (/\/channels\/[A-Za-z0-9]+\/delete/.test(currentActionURL)) { + currentAuditDesc = 'Deleted the channel/group with the URL ' + channelURL; + } else if (/\/channels\/[A-Za-z0-9]+\/add/.test(currentActionURL)) { + currentAuditDesc = 'Added ' + username + ' to the ' + channelName + ' channel/group'; + } else if (/\/channels\/[A-Za-z0-9]+\/remove/.test(currentActionURL)) { + currentAuditDesc = 'Removed ' + username + ' from the ' + channelName + ' channel/group'; + } - if (updateChannelInfo[0] === 'name') { - originalChannelName = updateChannelInfo[1]; + break; + } + } else if (currentActionURL.indexOf('/oauth') === 0) { + switch (currentActionURL) { + case '/oauth/register': + break; + case '/oauth/allow': + break; + case '/oauth/access_token': + break; + default: + break; } + } else if (currentActionURL.indexOf('/users') === 0) { + const userInfo = currentAudit.extra_info.split(' '); + switch (currentActionURL) { + case '/users/login': + if (userInfo[0] === 'attempt') { + currentAuditDesc = 'Login attempted'; + } else if (userInfo[0] === 'success') { + currentAuditDesc = 'Successful login attempt'; + } else if (userInfo[0]) { + currentAuditDesc = 'FAILED login attempt'; + } + + break; + case '/users/revoke_session': + const revokedSessionId = userInfo[0].split('=')[1]; + + currentAuditDesc = 'Revoked the session with id ' + revokedSessionId; + break; + case '/users/newimage': + currentAuditDesc = 'Updated your profile picture'; + break; + case '/users/update': + currentAuditDesc = 'Updated the general settings of your account'; + break; + case '/users/newpassword': + if (userInfo[0] === 'attempted') { + currentAuditDesc = 'Password change attempted'; + } else if (userInfo[0] === 'completed') { + currentAuditDesc = 'Password change success'; + } - currentAuditDesc = 'Updated the channel/group name for ' + originalChannelName; - break; + break; + case '/users/update_roles': + const userRoles = userInfo[0].split('=')[1]; - /* case '/channels/update_desc': - const updateChannelInfo = currentAudit.extra_info.split('='); - let originalChannelName = ''; + currentAuditDesc = 'Updated user role(s) to '; + if (userRoles.trim()) { + currentAuditDesc += userRoles; + } else { + currentAuditDesc += 'member'; + } - if (updateChannelInfo[0] === 'name') { - originalChannelName = updateChannelInfo[1]; + break; + case '/users/update_active': + break; + case '/users/send_password_reset': + break; + case '/users/reset_password': + break; + case '/users/update_notify': + break; + default: + break; + } + } else if (currentActionURL.indexOf('/hooks') === 0) { + switch (currentActionURL) { + case '/hooks/incoming/create': + break; + case '/hooks/incoming/delete': + break; + default: + break; + } + } else { + switch (currentActionURL) { + case '/logout': + break; + case '/verify_email': + break; + default: + break; } + } - currentAuditDesc = 'Updated the channel/group name for ' + originalChannelName; - break;*/ - case /\/channels\/[A-Za-z0-9]+\/delete/: - break; - case /\/channels\/[A-Za-z0-9]+\/add/: - break; - case /\/channels\/[A-Za-z0-9]+\/remove/: - break; - case '/oauth/register': - break; - case '/oauth/allow': - break; - case '/users/login': - break; - case '/users/revoke_session': - break; - case '/users/newimage': - break; - case '/users/update': - break; - case '/users/newpassword': - break; - case '/users/update_roles': - break; - case '/users/update_active': - break; - case '/users/send_password_reset': - break; - case '/users/reset_password': - break; - case '/users/update_notify': - break; - case '/logout': - break; - case '/hooks/incoming/create': - break; - case '/hooks/incoming/delete': - break; - case '/verify_email': - break; - case '/oauth/access_token': - break; - case '': - break; - default: + /* If all else fails... */ + if (!currentAuditDesc) { if (currentAudit.extra_info.indexOf('revoked_all=') >= 0) { // do stuff @@ -183,8 +248,6 @@ export default class AccessHistoryModal extends React.Component { } currentAuditDesc = currentActionDesc + ' ' + currentExtraInfoDesc; } - - break; } const currentDate = new Date(currentAudit.create_at); @@ -210,7 +273,11 @@ export default class AccessHistoryModal extends React.Component { if (this.state.moreInfo[i]) { if (!currentAudit.session_id && currentAudit.action.search('/users/login') !== -1) { - currentAudit.session_id = 'N/A (Login attempt)'; + if (currentAudit.extra_info === 'attempt') { + currentAudit.session_id = 'N/A (Login attempt)'; + } else { + currentAudit.session_id = 'N/A (Login failure)'; + } } moreInfo = ( -- cgit v1.2.3-1-g7c22 From e7e83b44d8be1f0081b70274a351486bade35d5b Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Tue, 13 Oct 2015 11:15:44 -0700 Subject: Added more individual audit handling, primarily involving the user --- web/react/components/access_history_modal.jsx | 56 ++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 5 deletions(-) (limited to 'web') diff --git a/web/react/components/access_history_modal.jsx b/web/react/components/access_history_modal.jsx index cb50ee4e7..4a169258a 100644 --- a/web/react/components/access_history_modal.jsx +++ b/web/react/components/access_history_modal.jsx @@ -16,6 +16,7 @@ export default class AccessHistoryModal extends React.Component { this.onHide = this.onHide.bind(this); this.onShow = this.onShow.bind(this); this.formatAuditInfo = this.formatAuditInfo.bind(this); + this.handleRevokedSession = this.handleRevokedSession.bind(this); const state = this.getStateFromStoresForAudits(); state.moreInfo = []; @@ -54,6 +55,9 @@ export default class AccessHistoryModal extends React.Component { newMoreInfo[index] = true; this.setState({moreInfo: newMoreInfo}); } + handleRevokedSession(sessionId) { + return 'The session with id ' + sessionId + ' was revoked'; + } formatAuditInfo(currentAudit) { const currentActionURL = currentAudit.action.replace(/\/api\/v[1-9]/, ''); @@ -142,6 +146,7 @@ export default class AccessHistoryModal extends React.Component { break; } } else if (currentActionURL.indexOf('/oauth') === 0) { + /* NEEDS TO BE DONE */ switch (currentActionURL) { case '/oauth/register': break; @@ -166,9 +171,7 @@ export default class AccessHistoryModal extends React.Component { break; case '/users/revoke_session': - const revokedSessionId = userInfo[0].split('=')[1]; - - currentAuditDesc = 'Revoked the session with id ' + revokedSessionId; + currentAuditDesc = this.handleRevokedSession(userInfo[0].split('=')[1]); break; case '/users/newimage': currentAuditDesc = 'Updated your profile picture'; @@ -196,30 +199,73 @@ export default class AccessHistoryModal extends React.Component { break; case '/users/update_active': + const updateType = userInfo[0].split('=')[0]; + const updateField = userInfo[0].split('=')[1]; + + /* Either describes account activation/deactivation or a revoked session as part of an account deactivation */ + if (updateType === 'active') { + if (updateField === 'true') { + currentAuditDesc = 'Account made active'; + } else if (updateField === 'false') { + currentAuditDesc = 'Account made inactive'; + } + + const actingUserInfo = userInfo[1].split('='); + if (actingUserInfo[0] === 'session_user') { + const actingUser = UserStore.getProfile(actingUserInfo[1]); + const currentUser = UserStore.getCurrentUser(); + if (currentUser && actingUser && (Utils.isAdmin(currentUser.roles) || Utils.isSystemAdmin(currentUser.roles))) { + currentAuditDesc += ' by ' + actingUser.username; + } else if (currentUser && actingUser) { + currentAuditDesc += ' by an admin'; + } + } + } else if (updateType === 'session_id') { + currentAuditDesc = this.handleRevokedSession(updateField); + } + break; case '/users/send_password_reset': + currentAuditDesc = 'Sent an email to ' + userInfo[0].split('=')[1] + ' to reset your password'; break; case '/users/reset_password': + + /* NEEDS TO BE TESTED! */ + if (userInfo[0] === 'attempted') { + currentAuditDesc = 'Password reset attempted'; + } else if (userInfo[0] === 'completed') { + currentAuditDesc = 'Password reset success'; + } + break; case '/users/update_notify': + currentAuditDesc = 'Updated your global notification settings'; break; default: break; } } else if (currentActionURL.indexOf('/hooks') === 0) { + /* NEEDS TO BE TESTED */ switch (currentActionURL) { case '/hooks/incoming/create': + currentAuditDesc = 'Attempted to create a webhook'; + currentAuditDesc = 'Successfully created a webhook'; break; case '/hooks/incoming/delete': + currentAuditDesc = 'Attempted to delete a webhook'; + currentAuditDesc = 'Successfully deleted a webhook'; break; default: break; } } else { + /* NEEDS TO BE TESTED */ switch (currentActionURL) { case '/logout': + currentAuditDesc = 'Logged out of your account'; break; case '/verify_email': + currentAuditDesc = 'Sucessfully verified your email address'; break; default: break; @@ -228,9 +274,9 @@ export default class AccessHistoryModal extends React.Component { /* If all else fails... */ if (!currentAuditDesc) { + /* Currently not called anywhere */ if (currentAudit.extra_info.indexOf('revoked_all=') >= 0) { - - // do stuff + currentAuditDesc = 'Revoked all current sessions for the team'; } else { let currentActionDesc = ' '; if (currentActionURL && currentActionURL.lastIndexOf('/') !== -1) { -- cgit v1.2.3-1-g7c22 From ae0d4dce7bc42f75568d65aa66c8db244f8f329f Mon Sep 17 00:00:00 2001 From: hmhealey Date: Wed, 14 Oct 2015 16:57:03 -0400 Subject: Updated More Direct Channels modal and converted it to ReactBootstrap --- web/react/components/more_direct_channels.jsx | 350 ++++++++++++++++++-------- web/react/components/sidebar.jsx | 69 +++-- web/react/pages/channel.jsx | 6 - web/react/utils/utils.jsx | 12 + web/sass-files/sass/partials/_modal.scss | 46 ++++ 5 files changed, 346 insertions(+), 137 deletions(-) (limited to 'web') diff --git a/web/react/components/more_direct_channels.jsx b/web/react/components/more_direct_channels.jsx index bc610cd60..99babe714 100644 --- a/web/react/components/more_direct_channels.jsx +++ b/web/react/components/more_direct_channels.jsx @@ -1,133 +1,273 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -var TeamStore = require('../stores/team_store.jsx'); -var Client = require('../utils/client.jsx'); -var Constants = require('../utils/constants.jsx'); -var AsyncClient = require('../utils/async_client.jsx'); -var PreferenceStore = require('../stores/preference_store.jsx'); -var utils = require('../utils/utils.jsx'); +const AsyncClient = require('../utils/async_client.jsx'); +const ChannelStore = require('../stores/channel_store.jsx'); +const Constants = require('../utils/constants.jsx'); +const Client = require('../utils/client.jsx'); +const Modal = ReactBootstrap.Modal; +const PreferenceStore = require('../stores/preference_store.jsx'); +const TeamStore = require('../stores/team_store.jsx'); +const UserStore = require('../stores/user_store.jsx'); +const Utils = require('../utils/utils.jsx'); export default class MoreDirectChannels extends React.Component { constructor(props) { super(props); - this.state = {channels: [], loadingDMChannel: -1}; + this.handleFilterChange = this.handleFilterChange.bind(this); + this.handleHide = this.handleHide.bind(this); + this.handleShowDirectChannel = this.handleShowDirectChannel.bind(this); + this.handleUserChange = this.handleUserChange.bind(this); + + this.createRowForUser = this.createRowForUser.bind(this); + + this.state = { + users: this.getUsersFromStore(), + filter: '', + loadingDMChannel: -1 + }; + } + + getUsersFromStore() { + const currentId = UserStore.getCurrentId(); + const profiles = UserStore.getProfiles(); + const users = []; + + for (const id in profiles) { + if (id !== currentId) { + users.push(profiles[id]); + } + } + + users.sort((a, b) => a.username.localeCompare(b.username)); + + return users; } componentDidMount() { - $(React.findDOMNode(this.refs.modal)).on('show.bs.modal', (e) => { - var button = e.relatedTarget; - this.setState({channels: $(button).data('channels')}); // eslint-disable-line react/no-did-mount-set-state - }); + UserStore.addChangeListener(this.handleUserChange); } - handleJoinDirectChannel(channel) { - const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, channel.teammate_id, 'true'); - AsyncClient.savePreferences([preference]); + componentWillUnmount() { + UserStore.addChangeListener(this.handleUserChange); } - render() { - var directMessageItems = this.state.channels.map((channel, index) => { - var badge = ''; - var titleClass = ''; - var handleClick = null; - - if (channel.fake) { - // It's a direct message channel that doesn't exist yet so let's create it now - var otherUserId = utils.getUserIdFromChannelName(channel); - - if (this.state.loadingDMChannel === index) { - badge = ( - - ); - } + handleFilterChange() { + const filter = React.findDOMNode(this.refs.filter).value; - if (this.state.loadingDMChannel === -1) { - handleClick = (e) => { - e.preventDefault(); - this.setState({loadingDMChannel: index}); - this.handleJoinDirectChannel(channel); - - Client.createDirectChannel(channel, otherUserId, - (data) => { - $(React.findDOMNode(this.refs.modal)).modal('hide'); - this.setState({loadingDMChannel: -1}); - AsyncClient.getChannel(data.id); - utils.switchChannel(data); - }, - () => { - this.setState({loadingDMChannel: -1}); - window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name; - } - ); - }; - } - } else { - if (channel.unread) { - badge = {channel.unread}; - titleClass = 'unread-title'; + if (filter !== this.state.filter) { + this.setState({filter}); + } + } + + handleHide() { + if (this.props.onModalDismissed) { + this.props.onModalDismissed(); + } + + this.setState({filter: ''}); + } + + handleShowDirectChannel(teammate, e) { + if (this.state.loadingDMChannel !== -1) { + return; + } + + e.preventDefault(); + + const channelName = Utils.getDirectChannelName(UserStore.getCurrentId(), teammate.id); + let channel = ChannelStore.getByName(channelName); + + const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, teammate.id, 'true'); + AsyncClient.savePreferences([preference]); + + if (channel) { + Utils.switchChannel(channel); + + this.handleHide(); + } else { + this.setState({loadingDMChannel: teammate.id}); + + channel = { + name: channelName, + last_post_at: 0, + total_msg_count: 0, + type: 'D', + display_name: teammate.username, + teammate_id: teammate.id, + status: UserStore.getStatus(teammate.id) + }; + + Client.createDirectChannel( + channel, + teammate.id, + (data) => { + this.setState({loadingDMChannel: -1}); + + AsyncClient.getChannel(data.id); + Utils.switchChannel(data); + + this.handleHide(); + }, + () => { + this.setState({loadingDMChannel: -1}); + window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channelName; } + ); + } + } - handleClick = (e) => { - e.preventDefault(); - this.handleJoinDirectChannel(channel); - utils.switchChannel(channel); - $(React.findDOMNode(this.refs.modal)).modal('hide'); - }; - } + handleUserChange() { + this.setState({users: this.getUsersFromStore()}); + } + + createRowForUser(user) { + const details = []; + + const fullName = Utils.getFullName(user); + if (fullName) { + details.push( + + {fullName} + + ); + } - return ( -
  • - {badge}{channel.display_name} -
  • + if (user.nickname) { + const separator = fullName ? ' - ' : ''; + details.push( + + {separator + user.nickname} + ); - }); + } + + let joinButton; + if (this.state.loadingDMChannel === user.id) { + joinButton = ( + + ); + } else { + joinButton = ( + + ); + } return ( -