From c84baf230ccb6f95fcf43798a3eb837c625639db Mon Sep 17 00:00:00 2001 From: nickago Date: Thu, 9 Jul 2015 08:34:36 -0700 Subject: [PATCH 01/90] Updated database schema for full utf8 compatibility --- store/sql_post_store.go | 5 +++++ store/sql_store.go | 12 ++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/store/sql_post_store.go b/store/sql_post_store.go index 7ada515d7e..5b9ebfdf21 100644 --- a/store/sql_post_store.go +++ b/store/sql_post_store.go @@ -35,6 +35,11 @@ func NewSqlPostStore(sqlStore *SqlStore) PostStore { } func (s SqlPostStore) UpgradeSchemaIfNeeded() { + + // These execs are for upgrading currently created databases to full utf8mb4 compliance + // Will be removed as seen fit for upgrading + s.GetMaster().Exec("ALTER TABLE Posts charset=utf8mb4") + s.GetMaster().Exec("ALTER TABLE Posts MODIFY COLUMN Message varchar(4000) CHARACTER SET utf8mb4") } func (s SqlPostStore) CreateIndexesIfNotExists() { diff --git a/store/sql_store.go b/store/sql_store.go index a0a1a9f237..7a2d059b91 100644 --- a/store/sql_store.go +++ b/store/sql_store.go @@ -24,6 +24,7 @@ import ( sqltrace "log" "math/rand" "os" + "strings" "time" ) @@ -81,7 +82,14 @@ func NewSqlStore() Store { func setupConnection(con_type string, driver string, dataSource string, maxIdle int, maxOpen int, trace bool) *gorp.DbMap { - db, err := dbsql.Open(driver, dataSource) + charset := "" + if strings.Index(dataSource, "?") > -1 { + charset = "&charset=utf8mb4,utf8" + } else { + charset = "?charset=utf8mb4,utf8" + } + + db, err := dbsql.Open(driver, dataSource+charset) if err != nil { l4g.Critical("Failed to open sql connection to '%v' err:%v", dataSource, err) time.Sleep(time.Second) @@ -104,7 +112,7 @@ func setupConnection(con_type string, driver string, dataSource string, maxIdle if driver == "sqlite3" { dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.SqliteDialect{}} } else if driver == "mysql" { - dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"}} + dbmap = &gorp.DbMap{Db: db, TypeConverter: mattermConverter{}, Dialect: gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8MB4"}} } else { l4g.Critical("Failed to create dialect specific driver") time.Sleep(time.Second) From 6a559febc292c272da24df440d80ae3b26a31d7f Mon Sep 17 00:00:00 2001 From: ralder Date: Thu, 9 Jul 2015 03:32:03 -0700 Subject: [PATCH 02/90] fix BrowserStore bug in actionOnItemsWithPrefix and refactor BrowserStore --- web/react/components/delete_post_modal.jsx | 4 +- web/react/components/edit_post_modal.jsx | 7 +- web/react/components/signup_team_complete.jsx | 11 +- web/react/components/signup_user_complete.jsx | 13 +- web/react/stores/browser_store.jsx | 159 ++++++++++-------- web/react/stores/channel_store.jsx | 64 ++----- web/react/stores/error_store.jsx | 11 +- web/react/stores/post_store.jsx | 59 ++----- web/react/stores/socket_store.jsx | 2 - web/react/stores/team_store.jsx | 16 +- web/react/stores/user_store.jsx | 120 +++---------- 11 files changed, 153 insertions(+), 313 deletions(-) diff --git a/web/react/components/delete_post_modal.jsx b/web/react/components/delete_post_modal.jsx index fefac12d72..11970bc2b3 100644 --- a/web/react/components/delete_post_modal.jsx +++ b/web/react/components/delete_post_modal.jsx @@ -56,13 +56,13 @@ module.exports = React.createClass({ $(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) { var newState = {}; if(BrowserStore.getItem('edit_state_transfer')) { - newState = JSON.parse(BrowserStore.getItem('edit_state_transfer')); + newState = BrowserStore.getItem('edit_state_transfer'); BrowserStore.removeItem('edit_state_transfer'); } else { var button = e.relatedTarget; newState = { title: $(button).attr('data-title'), channel_id: $(button).attr('data-channelid'), post_id: $(button).attr('data-postid'), comments: $(button).attr('data-comments') }; } - self.setState(newState) + self.setState(newState); }); PostStore.addSelectedPostChangeListener(this._onChange); }, diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx index d741e189bb..21b75bb6ee 100644 --- a/web/react/components/edit_post_modal.jsx +++ b/web/react/components/edit_post_modal.jsx @@ -4,6 +4,7 @@ var Client = require('../utils/client.jsx'); var AsyncClient = require('../utils/async_client.jsx'); var Textbox = require('./textbox.jsx'); +var BrowserStore = require('../stores/browser_store.jsx'); module.exports = React.createClass({ handleEdit: function(e) { @@ -13,14 +14,14 @@ module.exports = React.createClass({ if (updatedPost.message.length === 0) { var tempState = this.state; delete tempState.editText; - BrowserStore.setItem('edit_state_transfer', JSON.stringify(tempState)); + BrowserStore.setItem('edit_state_transfer', tempState); $("#edit_post").modal('hide'); $("#delete_post").modal('show'); return; } - updatedPost.id = this.state.post_id - updatedPost.channel_id = this.state.channel_id + updatedPost.id = this.state.post_id; + updatedPost.channel_id = this.state.channel_id; Client.updatePost(updatedPost, function(data) { diff --git a/web/react/components/signup_team_complete.jsx b/web/react/components/signup_team_complete.jsx index 500ee231e1..9e2a139554 100644 --- a/web/react/components/signup_team_complete.jsx +++ b/web/react/components/signup_team_complete.jsx @@ -596,19 +596,14 @@ PasswordPage = React.createClass({ module.exports = React.createClass({ updateParent: function(state, skipSet) { - BrowserStore.setGlobalItem(this.props.hash, JSON.stringify(state)); + BrowserStore.setGlobalItem(this.props.hash, state); if (!skipSet) { this.setState(state); } }, getInitialState: function() { - var props = null; - try { - props = JSON.parse(BrowserStore.getGlobalItem(this.props.hash)); - } - catch(parse_error) { - } + var props = BrowserStore.getGlobalItem(this.props.hash); if (!props) { props = {}; @@ -628,7 +623,7 @@ module.exports = React.createClass({ props.data = this.props.data; } - return props ; + return props; }, render: function() { if (this.state.wizard == "welcome") { diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx index fb96cc99fd..fa0c26017b 100644 --- a/web/react/components/signup_user_complete.jsx +++ b/web/react/components/signup_user_complete.jsx @@ -17,7 +17,7 @@ module.exports = React.createClass({ return; } - var username_error = utils.isValidUsername(this.state.user.username) + var username_error = utils.isValidUsername(this.state.user.username); if (username_error === "Cannot use a reserved word as a username.") { this.setState({name_error: "This username is reserved, please choose a new one.", email_error: "", password_error: "", server_error: ""}); return; @@ -53,7 +53,7 @@ module.exports = React.createClass({ UserStore.setLastEmail(this.state.user.email); UserStore.setCurrentUser(data); if (this.props.hash > 0) - BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: "finished"})); + BrowserStore.setGlobalItem(this.props.hash, {wizard: "finished"}); window.location.href = '/channels/town-square'; }.bind(this), function(err) { @@ -73,12 +73,7 @@ module.exports = React.createClass({ ); }, getInitialState: function() { - var props = null; - try { - props = JSON.parse(BrowserStore.getGlobalItem(this.props.hash)); - } - catch(parse_error) { - } + var props = BrowserStore.getGlobalItem(this.props.hash); if (!props) { props = {}; @@ -91,7 +86,7 @@ module.exports = React.createClass({ props.original_email = this.props.email; } - return props ; + return props; }, render: function() { diff --git a/web/react/stores/browser_store.jsx b/web/react/stores/browser_store.jsx index 82cf9a9423..4d6eb0b8db 100644 --- a/web/react/stores/browser_store.jsx +++ b/web/react/stores/browser_store.jsx @@ -1,85 +1,104 @@ // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. -var UserStore = require('../stores/user_store.jsx'); -// Also change model/utils.go ETAG_ROOT_VERSION -var BROWSER_STORE_VERSION = '.1'; - -var _initialized = false; - -function _initialize() { - var currentVersion = localStorage.getItem("local_storage_version"); - if (currentVersion !== BROWSER_STORE_VERSION) { - localStorage.clear(); - sessionStorage.clear(); - localStorage.setItem("local_storage_version", BROWSER_STORE_VERSION); - } - _initialized = true; +var UserStore; +function getPrefix() { + if (!UserStore) UserStore = require('./user_store.jsx'); + return UserStore.getCurrentId() + '_'; } -module.exports.setItem = function(name, value) { - if (!_initialized) _initialize(); - var user_id = UserStore.getCurrentId(); - localStorage.setItem(user_id + "_" + name, value); -}; +// Also change model/utils.go ETAG_ROOT_VERSION +var BROWSER_STORE_VERSION = '.3'; -module.exports.getItem = function(name) { - if (!_initialized) _initialize(); - var user_id = UserStore.getCurrentId(); - return localStorage.getItem(user_id + "_" + name); -}; +module.exports = { + _initialized: false, -module.exports.removeItem = function(name) { - if (!_initialized) _initialize(); - var user_id = UserStore.getCurrentId(); - localStorage.removeItem(user_id + "_" + name); -}; + _initialize: function() { + var currentVersion = localStorage.getItem("local_storage_version"); + if (currentVersion !== BROWSER_STORE_VERSION) { + this.clear(); + localStorage.setItem("local_storage_version", BROWSER_STORE_VERSION); + } + this._initialized = true; + }, -module.exports.setGlobalItem = function(name, value) { - if (!_initialized) _initialize(); - localStorage.setItem(name, value); -}; + getItem: function(name, defaultValue) { + return this.getGlobalItem(getPrefix() + name, defaultValue); + }, -module.exports.getGlobalItem = function(name) { - if (!_initialized) _initialize(); - return localStorage.getItem(name); -}; + setItem: function(name, value) { + this.setGlobalItem(getPrefix() + name, value); + }, -module.exports.removeGlobalItem = function(name) { - if (!_initialized) _initialize(); - localStorage.removeItem(name); -}; + removeItem: function(name) { + if (!this._initialized) this._initialize(); -module.exports.clear = function() { - localStorage.clear(); - sessionStorage.clear(); -}; + localStorage.removeItem(getPrefix() + name); + }, -// Preforms the given action on each item that has the given prefix -// Signiture for action is action(key, value) -module.exports.actionOnItemsWithPrefix = function (prefix, action) { - var user_id = UserStore.getCurrentId(); - var id_len = user_id.length; - var prefix_len = prefix.length; - for (var key in localStorage) { - if (key.substring(id_len, id_len + prefix_len) === prefix) { - var userkey = key.substring(id_len); - action(userkey, BrowserStore.getItem(key)); + setGlobalItem: function(name, value) { + if (!this._initialized) this._initialize(); + + localStorage.setItem(name, JSON.stringify(value)); + }, + + getGlobalItem: function(name, defaultValue) { + if (!this._initialized) this._initialize(); + + var result = null; + try { + result = JSON.parse(localStorage.getItem(name)); + } catch (err) {} + + if (result === null && typeof defaultValue !== 'undefined') { + result = defaultValue; + } + + return result; + }, + + removeGlobalItem: function(name) { + if (!this._initialized) this._initialize(); + + localStorage.removeItem(name); + }, + + clear: function() { + localStorage.clear(); + sessionStorage.clear(); + }, + + /** + * Preforms the given action on each item that has the given prefix + * Signiture for action is action(key, value) + */ + actionOnItemsWithPrefix: function (prefix, action) { + if (!this._initialized) this._initialize(); + + var globalPrefix = getPrefix(); + var globalPrefixiLen = globalPrefix.length; + for (var key in localStorage) { + if (key.lastIndexOf(globalPrefix + prefix, 0) === 0) { + var userkey = key.substring(globalPrefixiLen); + action(userkey, this.getGlobalItem(key)); + } + } + }, + + isLocalStorageSupported: function() { + try { + sessionStorage.setItem("testSession", '1'); + sessionStorage.removeItem("testSession"); + + localStorage.setItem("testLocal", '1'); + if (localStorage.getItem("testLocal") != '1') { + return false; + } + localStorage.removeItem("testLocal", '1'); + + return true; + } catch (e) { + return false; } } }; - -module.exports.isLocalStorageSupported = function() { - try { - sessionStorage.setItem("testSession", '1'); - sessionStorage.removeItem("testSession"); - - localStorage.setItem("testLocal", '1'); - localStorage.removeItem("testLocal", '1'); - - return true; - } - catch (e) { - return false; - } -}; diff --git a/web/react/stores/channel_store.jsx b/web/react/stores/channel_store.jsx index 4429a5312c..4a27e5f171 100644 --- a/web/react/stores/channel_store.jsx +++ b/web/react/stores/channel_store.jsx @@ -16,6 +16,7 @@ var MORE_CHANGE_EVENT = 'change'; var EXTRA_INFO_EVENT = 'extra_info'; var ChannelStore = assign({}, EventEmitter.prototype, { + _current_id: null, emitChange: function() { this.emit(CHANGE_EVENT); }, @@ -88,10 +89,7 @@ var ChannelStore = assign({}, EventEmitter.prototype, { return this._getMoreChannels(); }, setCurrentId: function(id) { - if (id == null) - BrowserStore.removeItem("current_channel_id"); - else - BrowserStore.setItem("current_channel_id", id); + this._current_id = id; }, setLastVisitedName: function(name) { if (name == null) @@ -117,10 +115,10 @@ var ChannelStore = assign({}, EventEmitter.prototype, { this._storeChannelMembers(cm); }, getCurrentId: function() { - return BrowserStore.getItem("current_channel_id"); + return this._current_id; }, getCurrent: function() { - var currentId = ChannelStore.getCurrentId(); + var currentId = this.getCurrentId(); if (currentId != null) return this.get(currentId); @@ -165,49 +163,22 @@ var ChannelStore = assign({}, EventEmitter.prototype, { return extra; }, _storeChannels: function(channels) { - BrowserStore.setItem("channels", JSON.stringify(channels)); + BrowserStore.setItem("channels", channels); }, _getChannels: function() { - var channels = []; - try { - channels = JSON.parse(BrowserStore.getItem("channels")); - } - catch (err) { - } - - if (channels == null) { - channels = []; - } - - return channels; + return BrowserStore.getItem("channels", []); }, _storeChannelMembers: function(channelMembers) { - BrowserStore.setItem("channel_members", JSON.stringify(channelMembers)); + BrowserStore.setItem("channel_members", channelMembers); }, _getChannelMembers: function() { - var members = {}; - try { - members = JSON.parse(BrowserStore.getItem("channel_members")); - } - catch (err) { - } - - if (members == null) { - members = {}; - } - - return members; + return BrowserStore.getItem("channel_members", {}); }, _storeMoreChannels: function(channels) { - BrowserStore.setItem("more_channels", JSON.stringify(channels)); + BrowserStore.setItem("more_channels", channels); }, _getMoreChannels: function() { - var channels = null; - try { - channels = JSON.parse(BrowserStore.getItem("more_channels")); - } - catch (err) { - } + var channels = BrowserStore.getItem("more_channels"); if (channels == null) { channels = {}; @@ -217,21 +188,10 @@ var ChannelStore = assign({}, EventEmitter.prototype, { return channels; }, _storeExtraInfos: function(extraInfos) { - BrowserStore.setItem("extra_infos", JSON.stringify(extraInfos)); + BrowserStore.setItem("extra_infos", extraInfos); }, _getExtraInfos: function() { - var members = {}; - try { - members = JSON.parse(BrowserStore.getItem("extra_infos")); - } - catch (err) { - } - - if (members == null) { - members = {}; - } - - return members; + return BrowserStore.getItem("extra_infos", {}); } }); diff --git a/web/react/stores/error_store.jsx b/web/react/stores/error_store.jsx index 3aed6aef23..203b692ec4 100644 --- a/web/react/stores/error_store.jsx +++ b/web/react/stores/error_store.jsx @@ -29,18 +29,11 @@ var ErrorStore = assign({}, EventEmitter.prototype, { BrowserStore.removeItem("last_error"); }, getLastError: function() { - var error = null; - try { - error = JSON.parse(BrowserStore.getItem("last_error")); - } - catch (err) { - } - - return error; + return BrowserStore.getItem('last_error'); }, _storeLastError: function(error) { - BrowserStore.setItem("last_error", JSON.stringify(error)); + BrowserStore.setItem("last_error", error); }, }); diff --git a/web/react/stores/post_store.jsx b/web/react/stores/post_store.jsx index 8bf3fdcb20..e773bb6889 100644 --- a/web/react/stores/post_store.jsx +++ b/web/react/stores/post_store.jsx @@ -106,55 +106,27 @@ var PostStore = assign({}, EventEmitter.prototype, { this.emitChange(); }, _storePosts: function(channelId, posts) { - BrowserStore.setItem("posts_" + channelId, JSON.stringify(posts)); + BrowserStore.setItem("posts_" + channelId, posts); }, getPosts: function(channelId) { - var posts = null; - try { - posts = JSON.parse(BrowserStore.getItem("posts_" + channelId)); - } - catch (err) { - } - - return posts; + return BrowserStore.getItem("posts_" + channelId); }, storeSearchResults: function(results, is_mention_search) { - BrowserStore.setItem("search_results", JSON.stringify(results)); + BrowserStore.setItem("search_results", results); is_mention_search = is_mention_search ? true : false; // force to bool - BrowserStore.setItem("is_mention_search", JSON.stringify(is_mention_search)); + BrowserStore.setItem("is_mention_search", is_mention_search); }, getSearchResults: function() { - var results = null; - try { - results = JSON.parse(BrowserStore.getItem("search_results")); - } - catch (err) { - } - - return results; + return BrowserStore.getItem("search_results"); }, getIsMentionSearch: function() { - var result = false; - try { - result = JSON.parse(BrowserStore.getItem("is_mention_search")); - } - catch (err) { - } - - return result; + return BrowserStore.getItem("is_mention_search"); }, storeSelectedPost: function(post_list) { - BrowserStore.setItem("select_post", JSON.stringify(post_list)); + BrowserStore.setItem("select_post", post_list); }, getSelectedPost: function() { - var post_list = null; - try { - post_list = JSON.parse(BrowserStore.getItem("select_post")); - } - catch (err) { - } - - return post_list; + return BrowserStore.getItem("select_post"); }, storeSearchTerm: function(term) { BrowserStore.setItem("search_term", term); @@ -165,25 +137,24 @@ var PostStore = assign({}, EventEmitter.prototype, { storeCurrentDraft: function(draft) { var channel_id = ChannelStore.getCurrentId(); var user_id = UserStore.getCurrentId(); - BrowserStore.setItem("draft_" + channel_id + "_" + user_id, JSON.stringify(draft)); + BrowserStore.setItem("draft_" + channel_id + "_" + user_id, draft); }, getCurrentDraft: function() { var channel_id = ChannelStore.getCurrentId(); var user_id = UserStore.getCurrentId(); - return JSON.parse(BrowserStore.getItem("draft_" + channel_id + "_" + user_id)); + return BrowserStore.getItem("draft_" + channel_id + "_" + user_id); }, storeDraft: function(channel_id, user_id, draft) { - BrowserStore.setItem("draft_" + channel_id + "_" + user_id, JSON.stringify(draft)); + BrowserStore.setItem("draft_" + channel_id + "_" + user_id, draft); }, getDraft: function(channel_id, user_id) { - return JSON.parse(BrowserStore.getItem("draft_" + channel_id + "_" + user_id)); + return BrowserStore.getItem("draft_" + channel_id + "_" + user_id); }, clearDraftUploads: function() { BrowserStore.actionOnItemsWithPrefix("draft_", function (key, value) { - var d = JSON.parse(value); - if (d) { - d['uploadsInProgress'] = 0; - BrowserStore.setItem(key, JSON.stringify(d)); + if (value) { + value.uploadsInProgress = 0; + BrowserStore.setItem(key, value); } }); } diff --git a/web/react/stores/socket_store.jsx b/web/react/stores/socket_store.jsx index 39800ead5f..8ebb854c94 100644 --- a/web/react/stores/socket_store.jsx +++ b/web/react/stores/socket_store.jsx @@ -10,8 +10,6 @@ var client = require('../utils/client.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; -var BrowserStore = require('../stores/browser_store.jsx'); - var CHANGE_EVENT = 'change'; var conn; diff --git a/web/react/stores/team_store.jsx b/web/react/stores/team_store.jsx index c494cb5b54..b7199a4a8f 100644 --- a/web/react/stores/team_store.jsx +++ b/web/react/stores/team_store.jsx @@ -63,22 +63,10 @@ var TeamStore = assign({}, EventEmitter.prototype, { this._storeTeams(teams); }, _storeTeams: function(teams) { - BrowserStore.setItem("user_teams", JSON.stringify(teams)); + BrowserStore.setItem("user_teams", teams); }, _getTeams: function() { - var teams = {}; - - try { - teams = JSON.parse(BrowserStore.getItem("user_teams")); - } - catch (err) { - } - - if (teams == null) { - teams = {}; - } - - return teams; + return BrowserStore.getItem("user_teams", {}); } }); diff --git a/web/react/stores/user_store.jsx b/web/react/stores/user_store.jsx index e832b34c7f..93ddfec708 100644 --- a/web/react/stores/user_store.jsx +++ b/web/react/stores/user_store.jsx @@ -8,7 +8,7 @@ var client = require('../utils/client.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; -var BrowserStore = require('../stores/browser_store.jsx'); +var BrowserStore = require('./browser_store.jsx'); var CHANGE_EVENT = 'change'; var CHANGE_EVENT_SESSIONS = 'change_sessions'; @@ -18,6 +18,8 @@ var CHANGE_EVENT_STATUSES = 'change_statuses'; var UserStore = assign({}, EventEmitter.prototype, { + _current_id: null, + emitChange: function(userId) { this.emit(CHANGE_EVENT, userId); }, @@ -64,13 +66,10 @@ var UserStore = assign({}, EventEmitter.prototype, { this.removeListener(CHANGE_EVENT_STATUSES, callback); }, setCurrentId: function(id) { - if (id == null) - BrowserStore.removeGlobalItem("current_user_id"); - else - BrowserStore.setGlobalItem("current_user_id", id); + this._current_id = id; }, getCurrentId: function(skipFetch) { - var current_id = BrowserStore.getGlobalItem("current_user_id"); + var current_id = this._current_id; // this is a speical case to force fetch the // current user if it's missing @@ -97,21 +96,13 @@ var UserStore = assign({}, EventEmitter.prototype, { this.setCurrentId(user.id); }, getLastDomain: function() { - var last_domain = BrowserStore.getItem("last_domain"); - if (last_domain == null) { - last_domain = ""; - } - return last_domain; + return BrowserStore.getItem("last_domain", ''); }, setLastDomain: function(domain) { BrowserStore.setItem("last_domain", domain); }, getLastEmail: function() { - var last_email = BrowserStore.getItem("last_email"); - if (last_email == null) { - last_email = ""; - } - return last_email; + return BrowserStore.getItem("last_email", ''); }, setLastEmail: function(email) { BrowserStore.setItem("last_email", email); @@ -153,91 +144,36 @@ var UserStore = assign({}, EventEmitter.prototype, { this._storeProfiles(ps); }, _storeProfiles: function(profiles) { - BrowserStore.setGlobalItem("profiles", JSON.stringify(profiles)); + BrowserStore.setGlobalItem("profiles", profiles); var profileUsernameMap = {}; for (var id in profiles) { profileUsernameMap[profiles[id].username] = profiles[id]; } - BrowserStore.setGlobalItem("profileUsernameMap", JSON.stringify(profileUsernameMap)); + BrowserStore.setGlobalItem("profileUsernameMap", profileUsernameMap); }, _getProfiles: function() { - var profiles = {}; - try { - profiles = JSON.parse(BrowserStore.getGlobalItem("profiles")); - } - catch (err) { - } - - if (profiles == null) { - profiles = {}; - } - - return profiles; + return BrowserStore.getGlobalItem("profiles", {}); }, _getProfilesUsernameMap: function() { - var profileUsernameMap = {}; - try { - profileUsernameMap = JSON.parse(BrowserStore.getGlobalItem("profileUsernameMap")); - } - catch (err) { - } - - if (profileUsernameMap == null) { - profileUsernameMap = {}; - } - - return profileUsernameMap; + return BrowserStore.getGlobalItem("profileUsernameMap", {}); }, setSessions: function(sessions) { - BrowserStore.setItem("sessions", JSON.stringify(sessions)); + BrowserStore.setItem("sessions", sessions); }, getSessions: function() { - var sessions = []; - try { - sessions = JSON.parse(BrowserStore.getItem("sessions")); - } - catch (err) { - } - if (sessions == null) { - sessions = []; - } - - return sessions; + return BrowserStore.getItem("sessions", []); }, setAudits: function(audits) { - BrowserStore.setItem("audits", JSON.stringify(audits)); + BrowserStore.setItem("audits", audits); }, getAudits: function() { - var audits = []; - try { - audits = JSON.parse(BrowserStore.getItem("audits")); - } - catch (err) { - } - - if (audits == null) { - audits = []; - } - - return audits; + return BrowserStore.getItem("audits", []); }, setTeams: function(teams) { - BrowserStore.setItem("teams", JSON.stringify(teams)); + BrowserStore.setItem("teams", teams); }, getTeams: function() { - var teams = []; - try { - teams = JSON.parse(BrowserStore.getItem("teams")); - - } - catch (err) { - } - - if (teams == null) { - teams = []; - } - - return teams; + return BrowserStore.getItem("teams", []); }, getCurrentMentionKeys: function() { var user = this.getCurrentUser(); @@ -258,11 +194,7 @@ var UserStore = assign({}, EventEmitter.prototype, { } }, getLastVersion: function() { - var last_version = BrowserStore.getItem("last_version"); - if (last_version == null) { - last_version = ""; - } - return last_version; + return BrowserStore.getItem("last_version", ''); }, setLastVersion: function(version) { BrowserStore.setItem("last_version", version); @@ -272,7 +204,7 @@ var UserStore = assign({}, EventEmitter.prototype, { this.emitStatusesChange(); }, _setStatuses: function(statuses) { - BrowserStore.setItem("statuses", JSON.stringify(statuses)); + BrowserStore.setItem("statuses", statuses); }, setStatus: function(user_id, status) { var statuses = this.getStatuses(); @@ -281,18 +213,7 @@ var UserStore = assign({}, EventEmitter.prototype, { this.emitStatusesChange(); }, getStatuses: function() { - var statuses = {}; - try { - statuses = JSON.parse(BrowserStore.getItem("statuses")); - } - catch (err) { - } - - if (statuses == null) { - statuses = {}; - } - - return statuses; + return BrowserStore.getItem("statuses", {}); }, getStatus: function(id) { return this.getStatuses()[id]; @@ -341,4 +262,3 @@ UserStore.setMaxListeners(0); global.window.UserStore = UserStore; module.exports = UserStore; - From e79ade46b2c43c0a6ee1ecffac91ba348aac790d Mon Sep 17 00:00:00 2001 From: ralder Date: Fri, 10 Jul 2015 12:20:00 -0700 Subject: [PATCH 03/90] [webui] fix incorrect height for mentions list for reply textbox --- web/react/components/mention_list.jsx | 29 +++++++++++++-------- web/sass-files/sass/partials/_mentions.scss | 9 ++++--- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index ba2c53612d..103ff29bb2 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -9,7 +9,12 @@ var Mention = require('./mention.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; +var MAX_HEIGHT_LIST = 292; +var MAX_ITEMS_IN_LIST = 25; +var ITEM_HEIGHT = 36; + module.exports = React.createClass({ + displayName: "MentionList", componentDidMount: function() { PostStore.addMentionDataChangeListener(this._onChange); @@ -72,7 +77,7 @@ module.exports = React.createClass({ }, render: function() { var mentionText = this.state.mentionText; - if (mentionText === '-1') return (
); + if (mentionText === '-1') return null; var profiles = UserStore.getActiveOnlyProfiles(); var users = []; @@ -100,8 +105,7 @@ module.exports = React.createClass({ var mentions = {}; var index = 0; - for (var i = 0; i < users.length; i++) { - if (Object.keys(mentions).length >= 25) break; + for (var i = 0; i < users.length && index < MAX_ITEMS_IN_LIST; i++) { if (this.alreadyMentioned(users[i].username)) continue; var firstName = "", lastName = ""; @@ -127,17 +131,20 @@ module.exports = React.createClass({ } var numMentions = Object.keys(mentions).length; - if (numMentions < 1) return (
); + if (numMentions < 1) return null; - var height = (numMentions*36) + 4; - var width = $('#'+this.props.id).parent().width(); - var bottom = $(window).height() - $('#'+this.props.id).offset().top; - var left = $('#'+this.props.id).offset().left; - var max_height = $('#'+this.props.id).offset().top - 10; + var $mention_tab = $('#'+this.props.id); + var maxHeight = Math.min(MAX_HEIGHT_LIST, $mention_tab.offset().top - 10); + var style = { + height: Math.min(maxHeight, (numMentions*ITEM_HEIGHT) + 4), + width: $mention_tab.parent().width(), + bottom: $(window).height() - $mention_tab.offset().top, + left: $mention_tab.offset().left + }; return ( -
-
+
+
{ mentions }
diff --git a/web/sass-files/sass/partials/_mentions.scss b/web/sass-files/sass/partials/_mentions.scss index da46866c82..7e8c1869a6 100644 --- a/web/sass-files/sass/partials/_mentions.scss +++ b/web/sass-files/sass/partials/_mentions.scss @@ -11,13 +11,14 @@ position: absolute; z-index: 1060; .mentions-box { - max-height: 303px; - position:absolute; - background-color:#fff; + width: 100%; + height: 100%; + position: absolute; + background-color: #fff; border: $border-gray; overflow-x: hidden; overflow-y: scroll; - bottom:0; + bottom: 0; } } From 46d3515fad57f3c777b44d96077fec5a0ab944c1 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Fri, 10 Jul 2015 17:07:01 -0700 Subject: [PATCH 04/90] Changes to notifcation carriage return parsing --- web/react/components/sidebar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index 2095978e87..4a57ec263a 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -249,7 +249,7 @@ var SidebarLoggedIn = React.createClass({ var repRegex = new RegExp("
", "g"); var post = JSON.parse(msg.props.post); - var msg = post.message.replace(repRegex, "\n").split("\n")[0].replace("", "").replace("", ""); + var msg = post.message.replace(repRegex, "\n").replace("\n", "").replace("", "").replace("", ""); if (msg.length > 50) { msg = msg.substring(0,49) + "..."; } From dcea1de00661a9fba9129a35b4c6451234a18d9a Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Fri, 10 Jul 2015 17:32:13 -0700 Subject: [PATCH 05/90] Changed parser for desktop notifications to replace returns with a single space. --- web/react/components/sidebar.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index 4a57ec263a..934a4d22a3 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -249,7 +249,7 @@ var SidebarLoggedIn = React.createClass({ var repRegex = new RegExp("
", "g"); var post = JSON.parse(msg.props.post); - var msg = post.message.replace(repRegex, "\n").replace("\n", "").replace("", "").replace("", ""); + var msg = post.message.replace(repRegex, "\n").replace(/\n+/g, " ").replace("", "").replace("", ""); if (msg.length > 50) { msg = msg.substring(0,49) + "..."; } From 4cc9bfce5c8bf09f743f00421f47349b6519a25f Mon Sep 17 00:00:00 2001 From: ralder Date: Sat, 11 Jul 2015 08:32:02 -0700 Subject: [PATCH 06/90] [webui] fix loading animation for ie --- web/react/components/loading_screen.jsx | 24 +++++ web/react/components/more_channels.jsx | 15 +-- web/react/components/post.jsx | 13 +-- web/react/components/post_body.jsx | 2 +- web/react/components/post_list.jsx | 105 ++++++--------------- web/react/utils/utils.jsx | 39 +++++++- web/sass-files/sass/partials/_loading.scss | 50 ++-------- web/sass-files/sass/partials/_post.scss | 1 + 8 files changed, 109 insertions(+), 140 deletions(-) create mode 100644 web/react/components/loading_screen.jsx diff --git a/web/react/components/loading_screen.jsx b/web/react/components/loading_screen.jsx new file mode 100644 index 0000000000..5905e519b8 --- /dev/null +++ b/web/react/components/loading_screen.jsx @@ -0,0 +1,24 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +module.exports = React.createClass({ + displayName: "LoadingScreen", + propTypes: { + position: React.PropTypes.oneOf(['absolute', 'fixed', 'relative', 'static', 'inherit']) + }, + getDefaultProps: function() { + return { position: 'relative' }; + }, + render: function() { + return ( +
+
+

Loading

+
+
+
+
+
+ ); + } +}); diff --git a/web/react/components/more_channels.jsx b/web/react/components/more_channels.jsx index c3ddc76f35..007476f9b9 100644 --- a/web/react/components/more_channels.jsx +++ b/web/react/components/more_channels.jsx @@ -7,6 +7,7 @@ var client = require('../utils/client.jsx'); var asyncClient = require('../utils/async_client.jsx'); var UserStore = require('../stores/user_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx'); +var LoadingScreen = require('./loading_screen.jsx'); function getStateFromStores() { return { @@ -16,6 +17,8 @@ function getStateFromStores() { } module.exports = React.createClass({ + displayName: "MoreChannelsModal", + componentDidMount: function() { ChannelStore.addMoreChangeListener(this._onChange); $(this.refs.modal.getDOMNode()).on('shown.bs.modal', function (e) { @@ -90,7 +93,7 @@ module.exports = React.createClass({

{channel.display_name}

{channel.description}

- + ) })} @@ -100,15 +103,7 @@ module.exports = React.createClass({

No more channels to join

Click 'Create New Channel' to make a new one

) - :
-
-

Loading

-
-
-
-
-
- } + : } { server_error }
diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx index 04b5ba082c..32e1759dda 100644 --- a/web/react/components/post.jsx +++ b/web/react/components/post.jsx @@ -10,6 +10,7 @@ var UserStore = require('../stores/user_store.jsx'); var ActionTypes = Constants.ActionTypes; module.exports = React.createClass({ + displayName: "Post", componentDidMount: function() { $('.modal').on('show.bs.modal', function () { $('.modal-body').css('overflow-y', 'auto'); @@ -19,7 +20,7 @@ module.exports = React.createClass({ handleCommentClick: function(e) { e.preventDefault(); - data = {}; + var data = {}; data.order = [this.props.post.id]; data.posts = this.props.posts; @@ -48,7 +49,6 @@ module.exports = React.createClass({ var commentCount = 0; var commentRootId = parentPost ? post.root_id : post.id; - var rootUser = ""; for (var postId in posts) { if (posts[postId].root_id == commentRootId) { commentCount += 1; @@ -57,12 +57,7 @@ module.exports = React.createClass({ var error = this.state.error ?
: null; - if (this.props.sameRoot){ - rootUser = "same--root"; - } - else { - rootUser = "other--root"; - } + var rootUser = this.props.sameRoot ? "same--root" : "other--root"; var postType = ""; if (type != "Post"){ @@ -81,7 +76,7 @@ module.exports = React.createClass({
- : "" } + : null }
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx index 7d5ef4d338..cf542a98f7 100644 --- a/web/react/components/post_body.jsx +++ b/web/react/components/post_body.jsx @@ -120,7 +120,7 @@ module.exports = React.createClass({ return (
{ comment } -

{inner}

+

{inner}

{ filenames && filenames.length > 0 ?
{ postFiles } diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index d6dc9ce303..9e16797171 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -8,6 +8,7 @@ var UserProfile = require( './user_profile.jsx' ); var AsyncClient = require('../utils/async_client.jsx'); var CreatePost = require('./create_post.jsx'); var Post = require('./post.jsx'); +var LoadingScreen = require('./loading_screen.jsx'); var SocketStore = require('../stores/socket_store.jsx'); var utils = require('../utils/utils.jsx'); var Client = require('../utils/client.jsx'); @@ -26,37 +27,8 @@ function getStateFromStores() { }; } -function changeColor(col, amt) { - - var usePound = false; - - if (col[0] == "#") { - col = col.slice(1); - usePound = true; - } - - var num = parseInt(col,16); - - var r = (num >> 16) + amt; - - if (r > 255) r = 255; - else if (r < 0) r = 0; - - var b = ((num >> 8) & 0x00FF) + amt; - - if (b > 255) b = 255; - else if (b < 0) b = 0; - - var g = (num & 0x0000FF) + amt; - - if (g > 255) g = 255; - else if (g < 0) g = 0; - - return (usePound?"#":"") + String("000000" + (g | (b << 8) | (r << 16)).toString(16)).slice(-6); - -} - module.exports = React.createClass({ + displayName: "PostList", scrollPosition: 0, preventScrollTrigger: false, gotMorePosts: false, @@ -69,7 +41,7 @@ module.exports = React.createClass({ utils.changeCss('a.theme', 'color:'+user.props.theme+'; fill:'+user.props.theme+'!important;'); utils.changeCss('div.theme', 'background-color:'+user.props.theme+';'); utils.changeCss('.btn.btn-primary', 'background: ' + user.props.theme+';'); - utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + changeColor(user.props.theme, -10) +';'); + utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + utils.changeColor(user.props.theme, -10) +';'); utils.changeCss('.modal .modal-header', 'background: ' + user.props.theme+';'); utils.changeCss('.mention', 'background: ' + user.props.theme+';'); utils.changeCss('.mention-link', 'color: ' + user.props.theme+';'); @@ -161,24 +133,20 @@ module.exports = React.createClass({ $('body').off('click.userpopover'); }, resize: function() { + var post_holder = $(".post-list-holder-by-time")[0]; + this.preventScrollTrigger = true; if (this.gotMorePosts) { this.gotMorePosts = false; - var post_holder = $(".post-list-holder-by-time")[0]; - this.preventScrollTrigger = true; $(post_holder).scrollTop($(post_holder).scrollTop() + (post_holder.scrollHeight-this.oldScrollHeight) ); - $(post_holder).perfectScrollbar('update'); } else { - var post_holder = $(".post-list-holder-by-time")[0]; - this.preventScrollTrigger = true; if ($("#new_message")[0] && !this.scrolledToNew) { $(post_holder).scrollTop($(post_holder).scrollTop() + $("#new_message").offset().top - 63); - $(post_holder).perfectScrollbar('update'); this.scrolledToNew = true; } else { $(post_holder).scrollTop(post_holder.scrollHeight); - $(post_holder).perfectScrollbar('update'); } } + $(post_holder).perfectScrollbar('update'); }, _onChange: function() { var newState = getStateFromStores(); @@ -347,7 +315,10 @@ module.exports = React.createClass({
-

{"This is the start of your private message history with " + teammate_name + "." }
{"Private messages and files shared here are not shown to people outside this area."}

+

+ {"This is the start of your private message history with " + teammate_name + "." }
+ {"Private messages and files shared here are not shown to people outside this area."} +

); } else { @@ -410,7 +381,7 @@ module.exports = React.createClass({ { channel.type === 'P' ? " Only invited members can see this private group." : " Any member can join and read this channel." }
Set a description - Invite others to this {ui_type} + Invite others to this {ui_type}

); @@ -420,37 +391,35 @@ module.exports = React.createClass({ var postCtls = []; - if (posts != undefined) { + if (posts) { var previousPostDay = posts[order[order.length-1]] ? utils.getDateForUnixTicks(posts[order[order.length-1]].create_at): new Date(); - var currentPostDay = new Date(); + var currentPostDay; for (var i = order.length-1; i >= 0; i--) { var post = posts[order[i]]; - var parentPost; + var parentPost = post.parent_id ? posts[post.parent_id] : null; - if (post.parent_id) { - parentPost = posts[post.parent_id]; - } else { - parentPost = null; + var sameUser = ''; + var sameRoot = false; + var hideProfilePic = false; + var prevPost = (i < order.length - 1) ? posts[order[i + 1]] : null; + + if (prevPost) { + sameUser = (prevPost.user_id === post.user_id) && (post.create_at - prevPost.create_at <= 1000*60*5) ? "same--user" : ""; + sameRoot = utils.isComment(post) && (prevPost.id === post.root_id || prevPost.root_id === post.root_id); + + // we only hide the profile pic if the previous post is not a comment, the current post is not a comment, and the previous post was made by the same user as the current post + hideProfilePic = (prevPost.user_id === post.user_id) && !utils.isComment(prevPost) && !utils.isComment(post); } - var sameUser = i < order.length-1 && posts[order[i+1]].user_id === post.user_id && post.create_at - posts[order[i+1]].create_at <= 1000*60*5 ? "same--user" : ""; - var sameRoot = i < order.length-1 && post.root_id != "" && (posts[order[i+1]].id === post.root_id || posts[order[i+1]].root_id === post.root_id) ? true : false; - - // we only hide the profile pic if the previous post is not a comment, the current post is not a comment, and the previous post was made by the same user as the current post - var hideProfilePic = i < order.length-1 && posts[order[i+1]].user_id === post.user_id && posts[order[i+1]].root_id === '' && post.root_id === ''; - // check if it's the last comment in a consecutive string of comments on the same post - var isLastComment = false; - if (utils.isComment(post)) { - // it is the last comment if it is last post in the channel or the next post has a different root post - isLastComment = (i === 0 || posts[order[i-1]].root_id != post.root_id); - } + // it is the last comment if it is last post in the channel or the next post has a different root post + var isLastComment = utils.isComment(post) && (i === 0 || posts[order[i-1]].root_id != post.root_id); var postCtl = ; currentPostDay = utils.getDateForUnixTicks(post.create_at); - if(currentPostDay.getDate() !== previousPostDay.getDate() || currentPostDay.getMonth() !== previousPostDay.getMonth() || currentPostDay.getFullYear() !== previousPostDay.getFullYear()) { + if (currentPostDay.toDateString() != previousPostDay.toDateString()) { postCtls.push(

@@ -469,20 +438,10 @@ module.exports = React.createClass({ ); } postCtls.push(postCtl); - previousPostDay = utils.getDateForUnixTicks(post.create_at); + previousPostDay = currentPostDay; } - } - else { - postCtls.push( -
-
-

Loading

-
-
-
-
-
- ); + } else { + postCtls.push(); } return ( @@ -497,5 +456,3 @@ module.exports = React.createClass({ ); } }); - - diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 5ded0e76f5..19c074606f 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -225,7 +225,7 @@ module.exports.extractLinks = function(text) { } return { "links": links, "text": text }; -} +} module.exports.escapeRegExp = function(string) { return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); @@ -671,13 +671,13 @@ module.exports.isValidUsername = function (name) { error = "First character must be a letter."; } - else + else { var lowerName = name.toLowerCase().trim(); - for (var i = 0; i < Constants.RESERVED_USERNAMES.length; i++) + for (var i = 0; i < Constants.RESERVED_USERNAMES.length; i++) { - if (lowerName === Constants.RESERVED_USERNAMES[i]) + if (lowerName === Constants.RESERVED_USERNAMES[i]) { error = "Cannot use a reserved word as a username."; break; @@ -782,3 +782,34 @@ module.exports.getHomeLink = function() { parts[0] = "www"; return window.location.protocol + "//" + parts.join("."); } + + +module.exports.changeColor =function(col, amt) { + + var usePound = false; + + if (col[0] == "#") { + col = col.slice(1); + usePound = true; + } + + var num = parseInt(col,16); + + var r = (num >> 16) + amt; + + if (r > 255) r = 255; + else if (r < 0) r = 0; + + var b = ((num >> 8) & 0x00FF) + amt; + + if (b > 255) b = 255; + else if (b < 0) b = 0; + + var g = (num & 0x0000FF) + amt; + + if (g > 255) g = 255; + else if (g < 0) g = 0; + + return (usePound?"#":"") + String("000000" + (g | (b << 8) | (r << 16)).toString(16)).slice(-6); + +}; diff --git a/web/sass-files/sass/partials/_loading.scss b/web/sass-files/sass/partials/_loading.scss index bc819e8f5d..d710557224 100644 --- a/web/sass-files/sass/partials/_loading.scss +++ b/web/sass-files/sass/partials/_loading.scss @@ -2,13 +2,8 @@ display: table; width: 100%; height: 100%; - position: absolute; - @include box-sizing(border-box); + padding: 60px; text-align: center; - &.loading-screen--channel { - position: relative; - padding: 4em 0 3.5em; - } .loading__content { display: table-cell; vertical-align: middle; @@ -19,11 +14,7 @@ margin: 0 0.2em 0; display: inline-block; } - } -} -.loading-screen { - .loading__content { .round { background-color: #444; width: 4px; @@ -32,43 +23,18 @@ margin: 0 2px; opacity: 0.1; @include border-radius(10px); - -moz-animation: move 0.75s infinite linear; - -webkit-animation: move 0.75s infinite linear; + @include animation(move 0.75s infinite linear); } - #round_1 { - -moz-animation-delay: .2s; - -webkit-animation-delay: .2s; - } - - #round_2 { - -moz-animation-delay: .4s; - -webkit-animation-delay: .4s; - } - - #round_3 { - -moz-animation-delay: .6s; - -webkit-animation-delay: .6s; - } - - @-moz-keyframes move { - 0% { - opacity: 1; + @for $i from 1 through 3 { + .round-#{$i} { + @include animation-delay(.2s*$i); } - - 100% { - opacity: 0.1; - }; } - @-webkit-keyframes move { - 0% { - opacity: 1; - } - - 100% { - opacity: 0.1; - }; + @include keyframes(move) { + from { opacity: 1; } + to { opacity: 0.1; } } } } diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss index d0c5363635..40ed40b49a 100644 --- a/web/sass-files/sass/partials/_post.scss +++ b/web/sass-files/sass/partials/_post.scss @@ -119,6 +119,7 @@ body.ios { table-layout: fixed; width: 100%; min-height: 100%; + height: 100%; .post-list__content { display: table-cell; vertical-align: bottom; From 252d0f3924dd19aa4dd1900c6c00c41c84755d1e Mon Sep 17 00:00:00 2001 From: =Corey Hulen Date: Sun, 12 Jul 2015 23:36:52 -0800 Subject: [PATCH 07/90] Fixes mm-1415 adding email bypass flag --- api/team.go | 2 +- api/user.go | 2 +- config/config.json | 3 +- config/config_docker.json | 3 +- utils/config.go | 1 + utils/mail.go | 13 ++++---- web/react/components/signup_user_complete.jsx | 33 +++++++++---------- 7 files changed, 29 insertions(+), 28 deletions(-) diff --git a/api/team.go b/api/team.go index c4a0ca1811..f9aeecd7e0 100644 --- a/api/team.go +++ b/api/team.go @@ -68,7 +68,7 @@ func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) { return } - if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV { + if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV || utils.Cfg.EmailSettings.ByPassEmail { m["follow_link"] = bodyPage.Props["Link"] } diff --git a/api/user.go b/api/user.go index 483ae67b52..3d1a2d3aea 100644 --- a/api/user.go +++ b/api/user.go @@ -293,7 +293,7 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !user.EmailVerified { + if !user.EmailVerified && !utils.Cfg.EmailSettings.ByPassEmail { c.Err = model.NewAppError("login", "Login failed because email address has not been verified", extraInfo) c.Err.StatusCode = http.StatusForbidden return diff --git a/config/config.json b/config/config.json index b0a019e8de..d5bb7e5539 100644 --- a/config/config.json +++ b/config/config.json @@ -54,11 +54,12 @@ "ProfileHeight": 128 }, "EmailSettings": { + "ByPassEmail" : true, "SMTPUsername": "", "SMTPPassword": "", "SMTPServer": "", "UseTLS": false, - "FeedbackEmail": "feedback@xxxxxxmustbefilledin.com", + "FeedbackEmail": "", "FeedbackName": "", "ApplePushServer": "", "ApplePushCertPublic": "", diff --git a/config/config_docker.json b/config/config_docker.json index 85f0d9c73b..91ed0ef10c 100644 --- a/config/config_docker.json +++ b/config/config_docker.json @@ -54,9 +54,10 @@ "ProfileHeight": 128 }, "EmailSettings": { + "ByPassEmail" : true, "SMTPUsername": "", "SMTPPassword": "", - "SMTPServer": "localhost:25", + "SMTPServer": "", "UseTLS": false, "FeedbackEmail": "", "FeedbackName": "", diff --git a/utils/config.go b/utils/config.go index eb2ae30505..97cb99f3ef 100644 --- a/utils/config.go +++ b/utils/config.go @@ -77,6 +77,7 @@ type ImageSettings struct { } type EmailSettings struct { + ByPassEmail bool SMTPUsername string SMTPPassword string SMTPServer string diff --git a/utils/mail.go b/utils/mail.go index 3cd37ffef8..0fe7042b76 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -8,14 +8,14 @@ import ( "crypto/tls" "fmt" "github.com/mattermost/platform/model" + "html" "net" "net/mail" "net/smtp" - "html" ) func CheckMailSettings() *model.AppError { - if len(Cfg.EmailSettings.SMTPServer) == 0 { + if len(Cfg.EmailSettings.SMTPServer) == 0 || Cfg.EmailSettings.ByPassEmail { return model.NewAppError("CheckMailSettings", "No email settings present, mail will not be sent", "") } conn, err := connectToSMTPServer() @@ -79,6 +79,10 @@ func newSMTPClient(conn net.Conn) (*smtp.Client, *model.AppError) { func SendMail(to, subject, body string) *model.AppError { + if len(Cfg.EmailSettings.SMTPServer) == 0 || Cfg.EmailSettings.ByPassEmail { + return nil + } + fromMail := mail.Address{"", Cfg.EmailSettings.FeedbackEmail} toMail := mail.Address{"", to} @@ -95,11 +99,6 @@ func SendMail(to, subject, body string) *model.AppError { } message += "\r\n" + body + "" - if len(Cfg.EmailSettings.SMTPServer) == 0 { - l4g.Warn("Skipping sending of email because EmailSettings are not configured") - return nil - } - conn, err1 := connectToSMTPServer() if err1 != nil { return err1 diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx index fb96cc99fd..ef1eb1c620 100644 --- a/web/react/components/signup_user_complete.jsx +++ b/web/react/components/signup_user_complete.jsx @@ -46,25 +46,24 @@ module.exports = React.createClass({ function(data) { client.track('signup', 'signup_user_02_complete'); - if (data.email_verified) { - client.loginByEmail(this.props.domain, this.state.user.email, this.state.user.password, - function(data) { - UserStore.setLastDomain(this.props.domain); - UserStore.setLastEmail(this.state.user.email); - UserStore.setCurrentUser(data); - if (this.props.hash > 0) - BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: "finished"})); - window.location.href = '/channels/town-square'; - }.bind(this), - function(err) { + client.loginByEmail(this.props.domain, this.state.user.email, this.state.user.password, + function(data) { + UserStore.setLastDomain(this.props.domain); + UserStore.setLastEmail(this.state.user.email); + UserStore.setCurrentUser(data); + if (this.props.hash > 0) + BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: "finished"})); + window.location.href = '/channels/town-square'; + }.bind(this), + function(err) { + if (err.message == "Login failed because email address has not been verified") { + window.location.href = "/verify?email="+ encodeURIComponent(this.state.user.email) + "&domain=" + encodeURIComponent(this.props.domain); + } else { this.state.server_error = err.message; this.setState(this.state); - }.bind(this) - ); - } - else { - window.location.href = "/verify?email="+ encodeURIComponent(this.state.user.email) + "&domain=" + encodeURIComponent(this.props.domain); - } + } + }.bind(this) + ); }.bind(this), function(err) { this.state.server_error = err.message; From b26bc73c7880e691e77e9ca4169e9ed7735e3bc9 Mon Sep 17 00:00:00 2001 From: JoramWilander Date: Mon, 13 Jul 2015 08:32:25 -0400 Subject: [PATCH 08/90] should fix scrolling on forceupdate --- web/react/components/post.jsx | 2 +- web/react/components/post_list.jsx | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx index 04b5ba082c..2d25e31e02 100644 --- a/web/react/components/post.jsx +++ b/web/react/components/post.jsx @@ -85,7 +85,7 @@ module.exports = React.createClass({
- +
diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index 177e4a1db3..3409bad286 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -63,6 +63,8 @@ module.exports = React.createClass({ oldScrollHeight: 0, oldZoom: 0, scrolledToNew: false, + p: 0, + wasForced: false, componentDidMount: function() { var user = UserStore.getCurrentUser(); if (user.props && user.props.theme) { @@ -108,6 +110,12 @@ module.exports = React.createClass({ }); $(post_holder).scroll(function(e){ + if (self.wasForced) { + console.log('hit'); + $(post_holder).scrollTop(self.p); + $(post_holder).perfectScrollbar('update'); + self.wasForced = false; + } if (!self.preventScrollTrigger) { self.scrollPosition = $(post_holder).scrollTop() + $(post_holder).innerHeight(); } @@ -197,7 +205,10 @@ module.exports = React.createClass({ this.setState(newState); } else { // Updates the timestamp on each post + this.wasForced = true; + this.p = $(".post-list-holder-by-time").scrollTop(); this.forceUpdate() + //this.refs.post0.refs.info.forceUpdate(); } }, _onSocketChange: function(msg) { @@ -452,7 +463,7 @@ module.exports = React.createClass({ isLastComment = (i === 0 || posts[order[i-1]].root_id != post.root_id); } - var postCtl = ; + var postCtl = ; currentPostDay = utils.getDateForUnixTicks(post.create_at); if(currentPostDay.getDate() !== previousPostDay.getDate() || currentPostDay.getMonth() !== previousPostDay.getMonth() || currentPostDay.getFullYear() !== previousPostDay.getFullYear()) { From 614992dcb7b4a4cdcedefe3ca813a03b758a97f0 Mon Sep 17 00:00:00 2001 From: nickago Date: Mon, 13 Jul 2015 08:34:34 -0700 Subject: [PATCH 09/90] Added timestamp updates to right side and cleaned code --- web/react/components/post.jsx | 2 +- web/react/components/post_list.jsx | 22 +++++++++++----------- web/react/components/post_right.jsx | 18 ++++++++++++++++-- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx index 2d25e31e02..04b5ba082c 100644 --- a/web/react/components/post.jsx +++ b/web/react/components/post.jsx @@ -85,7 +85,7 @@ module.exports = React.createClass({
- +
diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index 59f33fb4dd..e186fb7065 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -63,7 +63,7 @@ module.exports = React.createClass({ oldScrollHeight: 0, oldZoom: 0, scrolledToNew: false, - p: 0, + preForcePosision: 0, wasForced: false, componentDidMount: function() { var user = UserStore.getCurrentUser(); @@ -80,6 +80,7 @@ module.exports = React.createClass({ PostStore.addChangeListener(this._onChange); ChannelStore.addChangeListener(this._onChange); + UserStore.addStatusesChangeListener(this._onTimeChange); SocketStore.addChangeListener(this._onSocketChange); $(".post-list-holder-by-time").perfectScrollbar(); @@ -110,8 +111,7 @@ module.exports = React.createClass({ $(post_holder).scroll(function(e){ if (self.wasForced) { - console.log('hit'); - $(post_holder).scrollTop(self.p); + $(post_holder).scrollTop(self.preForcePosision); $(post_holder).perfectScrollbar('update'); self.wasForced = false; } @@ -165,6 +165,7 @@ module.exports = React.createClass({ componentWillUnmount: function() { PostStore.removeChangeListener(this._onChange); ChannelStore.removeChangeListener(this._onChange); + UserStore.removeStatusesChangeListener(this._onTimeChange); SocketStore.removeChangeListener(this._onSocketChange); $('body').off('click.userpopover'); }, @@ -201,13 +202,7 @@ module.exports = React.createClass({ this.scrolledToNew = false; } this.setState(newState); - } else { - // Updates the timestamp on each post - this.wasForced = true; - this.p = $(".post-list-holder-by-time").scrollTop(); - this.forceUpdate() - //this.refs.post0.refs.info.forceUpdate(); - } + } }, _onSocketChange: function(msg) { if (msg.action == "posted") { @@ -271,6 +266,11 @@ module.exports = React.createClass({ AsyncClient.getProfiles(); } }, + _onTimeChange: function() { + this.wasForced = true; + this.preForcePosision = $(".post-list-holder-by-time").scrollTop(); + this.forceUpdate() + }, getMorePosts: function(e) { e.preventDefault(); @@ -461,7 +461,7 @@ module.exports = React.createClass({ isLastComment = (i === 0 || posts[order[i-1]].root_id != post.root_id); } - var postCtl = ; + var postCtl = ; currentPostDay = utils.getDateForUnixTicks(post.create_at); if(currentPostDay.getDate() !== previousPostDay.getDate() || currentPostDay.getMonth() !== previousPostDay.getMonth() || currentPostDay.getFullYear() !== previousPostDay.getFullYear()) { diff --git a/web/react/components/post_right.jsx b/web/react/components/post_right.jsx index 115ee87d4a..89a616d272 100644 --- a/web/react/components/post_right.jsx +++ b/web/react/components/post_right.jsx @@ -279,9 +279,11 @@ function getStateFromStores() { } module.exports = React.createClass({ + wasForced: false, componentDidMount: function() { PostStore.addSelectedPostChangeListener(this._onChange); PostStore.addChangeListener(this._onChangeAll); + UserStore.addStatusesChangeListener(this._onTimeChange); this.resize(); var self = this; $(window).resize(function(){ @@ -289,11 +291,15 @@ module.exports = React.createClass({ }); }, componentDidUpdate: function() { - this.resize(); + if(!this.wasForced){ + this.resize(); + wasForced = false + } }, componentWillUnmount: function() { PostStore.removeSelectedPostChangeListener(this._onChange); PostStore.removeChangeListener(this._onChangeAll); + UserStore.removeStatusesChangeListener(this._onTimeChange) }, _onChange: function() { if (this.isMounted()) { @@ -333,6 +339,14 @@ module.exports = React.createClass({ this.setState(getStateFromStores()); } }, + _onTimeChange: function() { + this.wasForced = true; + for (var key in this.refs) { + if(this.refs[key].forceUpdate != undefined) { + this.refs[key].forceUpdate(); + } + } + }, getInitialState: function() { return getStateFromStores(); }, @@ -392,7 +406,7 @@ module.exports = React.createClass({
{ posts_array.map(function(cpost) { - return + return })}
From c3aed620316241eba48ecd2fd1f36a309bcfdff9 Mon Sep 17 00:00:00 2001 From: nickago Date: Thu, 9 Jul 2015 16:37:03 -0700 Subject: [PATCH 10/90] Added timestamps to pictures to stop caching --- store/sql_user_store.go | 19 +++++++++++++++++++ store/store.go | 1 + web/react/components/post.jsx | 4 +++- web/react/components/post_list.jsx | 2 +- web/react/components/post_right.jsx | 3 ++- web/react/components/user_profile.jsx | 2 +- 6 files changed, 27 insertions(+), 4 deletions(-) diff --git a/store/sql_user_store.go b/store/sql_user_store.go index fc3d125c4f..77470946c6 100644 --- a/store/sql_user_store.go +++ b/store/sql_user_store.go @@ -150,6 +150,25 @@ func (us SqlUserStore) Update(user *model.User, allowActiveUpdate bool) StoreCha return storeChannel } +func (us SqlUserStore) UpdateUpdateAt(userId string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + if _, err := us.GetMaster().Exec("UPDATE Users SET UpdateAt = ? WHERE Id = ?", model.GetMillis(), userId); err != nil { + result.Err = model.NewAppError("SqlUserStore.UpdateUpdateAt", "We couldn't update the update_at", "user_id="+userId) + } else { + result.Data = userId + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + func (us SqlUserStore) UpdateLastPingAt(userId string, time int64) StoreChannel { storeChannel := make(StoreChannel) diff --git a/store/store.go b/store/store.go index 070ee05622..0ed0457887 100644 --- a/store/store.go +++ b/store/store.go @@ -77,6 +77,7 @@ type PostStore interface { type UserStore interface { Save(user *model.User) StoreChannel Update(user *model.User, allowRoleUpdate bool) StoreChannel + UpdateUpdateAt(userId string) StoreChannel UpdateLastPingAt(userId string, time int64) StoreChannel UpdateLastActivityAt(userId string, time int64) StoreChannel UpdateUserAndSessionActivity(userId string, sessionId string, time int64) StoreChannel diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx index 04b5ba082c..726fb1b024 100644 --- a/web/react/components/post.jsx +++ b/web/react/components/post.jsx @@ -74,12 +74,14 @@ module.exports = React.createClass({ currentUserCss = "current--user"; } + var timestamp = UserStore.getCurrentUser().update_at; + return (
{ !this.props.hideProfilePic ?
- +
: "" }
diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index d6dc9ce303..e6e0282090 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -342,7 +342,7 @@ module.exports = React.createClass({ more_messages = (
- +
diff --git a/web/react/components/post_right.jsx b/web/react/components/post_right.jsx index 115ee87d4a..d48b0f0ef0 100644 --- a/web/react/components/post_right.jsx +++ b/web/react/components/post_right.jsx @@ -67,6 +67,7 @@ RootPost = React.createClass({ var message = utils.textToJsx(this.props.post.message); var filenames = this.props.post.filenames; var isOwner = UserStore.getCurrentId() == this.props.post.user_id; + var timestamp = UserStore.getProfile(this.props.post.user_id).update_at; var type = "Post"; if (this.props.post.root_id.length > 0) { @@ -118,7 +119,7 @@ RootPost = React.createClass({ return (
- +
    diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx index 648960471a..89d0a80ff2 100644 --- a/web/react/components/user_profile.jsx +++ b/web/react/components/user_profile.jsx @@ -53,7 +53,7 @@ module.exports = React.createClass({ var name = this.props.overwriteName ? this.props.overwriteName : this.state.profile.username; - var data_content = ""; + var data_content = ""; if (!config.ShowEmail) { data_content += "
    Email not shared
    "; } else { From 7d6d9c297f21fa8cf0848c071bcdd13691fb3d9b Mon Sep 17 00:00:00 2001 From: nickago Date: Fri, 10 Jul 2015 09:56:41 -0700 Subject: [PATCH 11/90] Added User update function --- api/user.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/user.go b/api/user.go index 483ae67b52..bdd26e0970 100644 --- a/api/user.go +++ b/api/user.go @@ -135,6 +135,8 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) { user.EmailVerified = true } + user.EmailVerified = true + ruser := CreateUser(c, team, user) if c.Err != nil { return @@ -729,6 +731,8 @@ func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) { return } + Srv.Store.User().UpdateUpdateAt(c.Session.UserId) + c.LogAudit("") } From f5d188e1caa4b27eb4878de41e479199ca0cafda Mon Sep 17 00:00:00 2001 From: nickago Date: Fri, 10 Jul 2015 09:57:17 -0700 Subject: [PATCH 12/90] Added async get to channel swap --- web/react/utils/utils.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 5ded0e76f5..a5ed08d671 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -707,6 +707,7 @@ module.exports.switchChannel = function(channel, teammate_name) { AsyncClient.getChannels(true, true, true); AsyncClient.getChannelExtraInfo(true); AsyncClient.getPosts(true, channel.id); + AsyncClient.getProfiles(); $('.inner__wrap').removeClass('move--right'); $('.sidebar--left').removeClass('move--right'); From c485dad84f9a94c3d1261f7e2207baf071c2d3ca Mon Sep 17 00:00:00 2001 From: nickago Date: Mon, 13 Jul 2015 09:22:42 -0700 Subject: [PATCH 13/90] Added update function to image update and fixed corner cases --- web/react/components/member_list_item.jsx | 3 ++- web/react/components/member_list_team.jsx | 5 +++-- web/react/components/mention.jsx | 4 +++- web/react/components/post_right.jsx | 3 ++- web/react/components/search_results.jsx | 3 ++- web/react/utils/utils.jsx | 1 - 6 files changed, 12 insertions(+), 7 deletions(-) diff --git a/web/react/components/member_list_item.jsx b/web/react/components/member_list_item.jsx index 357fd49a87..cf8c71d7e6 100644 --- a/web/react/components/member_list_item.jsx +++ b/web/react/components/member_list_item.jsx @@ -23,6 +23,7 @@ module.exports = React.createClass({ var member = this.props.member; var isAdmin = this.props.isAdmin; var isMemberAdmin = member.roles.indexOf("admin") > -1; + var timestamp = UserStore.getCurrentUser().update_at; var invite; if (member.invited && this.props.handleInvite) { @@ -53,7 +54,7 @@ module.exports = React.createClass({ return (
    - + {member.username} {member.email} { invite } diff --git a/web/react/components/member_list_team.jsx b/web/react/components/member_list_team.jsx index cfb473e5ee..aa53c5db64 100644 --- a/web/react/components/member_list_team.jsx +++ b/web/react/components/member_list_team.jsx @@ -61,7 +61,8 @@ var MemberListTeamItem = React.createClass({ render: function() { var server_error = this.state.server_error ?
    : null; var user = this.props.user; - var currentRoles = "Member" + var currentRoles = "Member"; + var timestamp = UserStore.getCurrentUser().update_at; if (user.roles.length > 0) { currentRoles = user.roles.charAt(0).toUpperCase() + user.roles.slice(1); @@ -83,7 +84,7 @@ var MemberListTeamItem = React.createClass({ return (
    - + {user.full_name.trim() ? user.full_name : user.username} {user.full_name.trim() ? user.username : email}
    diff --git a/web/react/components/mention.jsx b/web/react/components/mention.jsx index 3c33ddf493..520b81cbb1 100644 --- a/web/react/components/mention.jsx +++ b/web/react/components/mention.jsx @@ -1,5 +1,6 @@ // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. +var UserStore = require("../stores/user_store.jsx"); module.exports = React.createClass({ handleClick: function() { @@ -7,8 +8,9 @@ module.exports = React.createClass({ }, render: function() { var icon; + var timestamp = UserStore.getCurrentUser().update_at; if (this.props.id != null) { - icon = ; + icon = ; } diff --git a/web/react/components/post_right.jsx b/web/react/components/post_right.jsx index d48b0f0ef0..408fbf83ac 100644 --- a/web/react/components/post_right.jsx +++ b/web/react/components/post_right.jsx @@ -228,11 +228,12 @@ CommentPost = React.createClass({ } var message = utils.textToJsx(this.props.post.message); + var timestamp = UserStore.getCurrentUser().update_at; return (
    - +
      diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx index 003a38b7ee..156cf0120e 100644 --- a/web/react/components/search_results.jsx +++ b/web/react/components/search_results.jsx @@ -76,6 +76,7 @@ SearchItem = React.createClass({ var message = utils.textToJsx(this.props.post.message, {searchTerm: this.props.term, noMentionHighlight: !this.props.isMentionSearch}); var channelName = ""; var channel = ChannelStore.get(this.props.post.channel_id) + var timestamp = UserStore.getCurrentUser().update_at; if (channel) { if (channel.type === 'D') { @@ -89,7 +90,7 @@ SearchItem = React.createClass({
      { channelName }
      - +
        diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index a5ed08d671..5ded0e76f5 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -707,7 +707,6 @@ module.exports.switchChannel = function(channel, teammate_name) { AsyncClient.getChannels(true, true, true); AsyncClient.getChannelExtraInfo(true); AsyncClient.getPosts(true, channel.id); - AsyncClient.getProfiles(); $('.inner__wrap').removeClass('move--right'); $('.sidebar--left').removeClass('move--right'); From aff43f43f8185ae6d5555ca9f99bfcc4bd3eb99e Mon Sep 17 00:00:00 2001 From: nickago Date: Mon, 13 Jul 2015 09:27:30 -0700 Subject: [PATCH 14/90] removed testing state --- api/user.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/api/user.go b/api/user.go index bdd26e0970..5b052e826c 100644 --- a/api/user.go +++ b/api/user.go @@ -135,8 +135,6 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) { user.EmailVerified = true } - user.EmailVerified = true - ruser := CreateUser(c, team, user) if c.Err != nil { return From 8595b915afae9c494f09bd45692102ae86306ffe Mon Sep 17 00:00:00 2001 From: nickago Date: Mon, 13 Jul 2015 11:24:39 -0700 Subject: [PATCH 15/90] Fixed 'this' typo and added semicolon --- web/react/components/post_right.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/react/components/post_right.jsx b/web/react/components/post_right.jsx index 89a616d272..0c37545291 100644 --- a/web/react/components/post_right.jsx +++ b/web/react/components/post_right.jsx @@ -293,7 +293,7 @@ module.exports = React.createClass({ componentDidUpdate: function() { if(!this.wasForced){ this.resize(); - wasForced = false + this.wasForced = false; } }, componentWillUnmount: function() { From f406beca8b501dad508d1ffb900bf994c4192086 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Fri, 10 Jul 2015 18:41:42 -0700 Subject: [PATCH 16/90] If a message has no text but has an attached image or file the desktop notification now says uploaded an image or uploaded a file respectively (if both, defaults to image) --- api/post.go | 14 ++++++++++++++ web/react/components/sidebar.jsx | 18 +++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/api/post.go b/api/post.go index 650f47062d..02f997166a 100644 --- a/api/post.go +++ b/api/post.go @@ -14,6 +14,7 @@ import ( "strconv" "strings" "time" + "path/filepath" ) func InitPost(r *mux.Router) { @@ -437,6 +438,19 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) { message := model.NewMessage(teamId, post.ChannelId, post.UserId, model.ACTION_POSTED) message.Add("post", post.ToJson()) + + if len(post.Filenames) != 0 { + message.Add("otherFile", "true") + + for _, filename := range post.Filenames { + ext := filepath.Ext(filename) + if model.IsFileExtImage(ext) { + message.Add("image", "true") + break + } + } + } + if len(mentionedUsers) != 0 { message.Add("mentions", model.ArrayToJson(mentionedUsers)) } diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index 934a4d22a3..cae9425d36 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -249,11 +249,27 @@ var SidebarLoggedIn = React.createClass({ var repRegex = new RegExp("
        ", "g"); var post = JSON.parse(msg.props.post); + var msgProps = msg.props; var msg = post.message.replace(repRegex, "\n").replace(/\n+/g, " ").replace("", "").replace("", ""); + if (msg.length > 50) { msg = msg.substring(0,49) + "..."; } - utils.notifyMe(title, username + " wrote: " + msg, channel); + + if (msg.length === 0) { + if (msgProps.image) { + utils.notifyMe(title, username + " uploaded an image", channel); + } + else if (msgProps.otherFile) { + utils.notifyMe(title, username + " uploaded a file", channel); + } + else { + utils.notifyMe(title, username + " did something new", channel); + } + } + else { + utils.notifyMe(title, username + " wrote: " + msg, channel); + } if (!user.notify_props || user.notify_props.desktop_sound === "true") { utils.ding(); } From 6e5126ba1d273e9576923a65d5b614314970f99f Mon Sep 17 00:00:00 2001 From: ralder Date: Mon, 13 Jul 2015 04:23:24 -0700 Subject: [PATCH 17/90] fix incorrect call for AsyncClient.dispatchError --- web/react/components/channel_header.jsx | 101 +++++++----------- web/react/components/post_list.jsx | 2 +- web/react/components/search_bar.jsx | 37 ++++--- web/react/components/search_results.jsx | 73 ++++++------- web/sass-files/sass/partials/_base.scss | 4 + web/sass-files/sass/partials/_responsive.scss | 8 +- web/sass-files/sass/partials/_variables.scss | 8 +- 7 files changed, 102 insertions(+), 131 deletions(-) diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx index 68de80228a..fe381a59e9 100644 --- a/web/react/components/channel_header.jsx +++ b/web/react/components/channel_header.jsx @@ -1,6 +1,7 @@ // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. + var ChannelStore = require('../stores/channel_store.jsx'); var UserStore = require('../stores/user_store.jsx'); var PostStore = require('../stores/post_store.jsx'); @@ -16,7 +17,7 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; -var ExtraMembers = React.createClass({ +var PopoverListMembers = React.createClass({ componentDidMount: function() { var originalLeave = $.fn.popover.Constructor.prototype.leave; $.fn.popover.Constructor.prototype.leave = function(obj) { @@ -35,30 +36,29 @@ var ExtraMembers = React.createClass({ $("#member_popover").popover({placement : 'bottom', trigger: 'click', html: true}); $('body').on('click', function (e) { - if ($(e.target.parentNode.parentNode)[0] !== $("#member_popover")[0] && $(e.target).parents('.popover.in').length === 0) { + if ($(e.target.parentNode.parentNode)[0] !== $("#member_popover")[0] && $(e.target).parents('.popover.in').length === 0) { $("#member_popover").popover('hide'); } }); - }, + render: function() { - var count = this.props.members.length == 0 ? "-" : this.props.members.length; - count = this.props.members.length > 19 ? "20+" : count; - var data_content = ""; - var sortedMembers = this.props.members; + var popoverHtml = ''; + var members = this.props.members; + var count = (members.length > 20) ? "20+" : (members.length || '-'); - if(sortedMembers) { - sortedMembers.sort(function(a,b) { + if (members) { + members.sort(function(a,b) { return a.username.localeCompare(b.username); - }) + }); - sortedMembers.forEach(function(m) { - data_content += "
        " + m.username + "
        "; + members.forEach(function(m) { + popoverHtml += "
        " + m.username + "
        "; }); } return ( -
        +
        {count}
        @@ -78,6 +78,7 @@ function getStateFromStores() { } module.exports = React.createClass({ + displayName: 'ChannelHeader', componentDidMount: function() { ChannelStore.addChangeListener(this._onChange); ChannelStore.addExtraInfoChangeListener(this._onChange); @@ -99,7 +100,7 @@ module.exports = React.createClass({ $(".channel-header__info .description").popover({placement : 'bottom', trigger: 'hover', html: true, delay: {show: 500, hide: 500}}); }, _onSocketChange: function(msg) { - if(msg.action === "new_user") { + if (msg.action === "new_user") { AsyncClient.getChannelExtraInfo(true); } }, @@ -107,15 +108,14 @@ module.exports = React.createClass({ return getStateFromStores(); }, handleLeave: function(e) { - var self = this; Client.leaveChannel(this.state.channel.id, function(data) { var townsquare = ChannelStore.getByName('town-square'); utils.switchChannel(townsquare); - }.bind(this), + }, function(err) { AsyncClient.dispatchError(err, "handleLeave"); - }.bind(this) + } ); }, searchMentions: function(e) { @@ -131,52 +131,29 @@ module.exports = React.createClass({ AppDispatcher.handleServerAction({ type: ActionTypes.RECIEVED_SEARCH_TERM, term: terms, - do_search: false + do_search: true, + is_mention_search: true }); - - Client.search( - terms, - function(data) { - AppDispatcher.handleServerAction({ - type: ActionTypes.RECIEVED_SEARCH, - results: data, - is_mention_search: true - }); - }, - function(err) { - dispatchError(err, "search"); - } - ); }, + render: function() { if (this.state.channel == null) { - return ( -
        - ); + return null; } var description = utils.textToJsx(this.state.channel.description, {"singleline": true, "noMentionHighlight": true}); var popoverContent = React.renderToString(); - var channelTitle = ""; + var channelTitle = this.state.channel.display_name; var channelName = this.state.channel.name; var currentId = UserStore.getCurrentId(); var isAdmin = this.state.memberChannel.roles.indexOf("admin") > -1 || this.state.memberTeam.roles.indexOf("admin") > -1; - var searchForm = ; - var isDirect = false; + var isDirect = (this.state.channel.type === 'D'); - if (this.state.channel.type === 'O') { - channelTitle = this.state.channel.display_name; - } else if (this.state.channel.type === 'P') { - channelTitle = this.state.channel.display_name; - } else if (this.state.channel.type === 'D') { - isDirect = true; + if (isDirect) { if (this.state.users.length > 1) { - if (this.state.users[0].id === UserStore.getCurrentId()) { - channelTitle = ; - } else { - channelTitle = ; - } + var contact = this.state.users[((this.state.users[0].id === currentId) ? 1 : 0)]; + channelTitle = ; } } @@ -196,21 +173,21 @@ module.exports = React.createClass({
      • Add Members
      • { isAdmin ?
      • Manage Members
      • - : "" + : null } -
      • Set Channel Description...
      • -
      • Notification Preferences
      • +
      • Set Channel Description...
      • +
      • Notification Preferences
      • { isAdmin && channelName != Constants.DEFAULT_CHANNEL ? -
      • Rename Channel...
      • - : "" +
      • Rename Channel...
      • + : null } { isAdmin && channelName != Constants.DEFAULT_CHANNEL ? -
      • Delete Channel...
      • - : "" +
      • Delete Channel...
      • + : null } { channelName != Constants.DEFAULT_CHANNEL ?
      • Leave Channel
      • - : "" + : null }
      @@ -220,14 +197,14 @@ module.exports = React.createClass({ {channelTitle} } - - { searchForm } + + -
      + @@ -237,5 +214,3 @@ module.exports = React.createClass({ ); } }); - - diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index e6e0282090..2a01b80893 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -297,7 +297,7 @@ module.exports = React.createClass({ }, function(err) { $(self.refs.loadmore.getDOMNode()).text("Load more messages"); - dispatchError(err, "getPosts"); + AsyncClient.dispatchError(err, "getPosts"); } ); }, diff --git a/web/react/components/search_bar.jsx b/web/react/components/search_bar.jsx index cddb738f94..ab7e99d609 100644 --- a/web/react/components/search_bar.jsx +++ b/web/react/components/search_bar.jsx @@ -3,6 +3,7 @@ var client = require('../utils/client.jsx'); +var AsyncClient = require('../utils/async_client.jsx'); var PostStore = require('../stores/post_store.jsx'); var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var utils = require('../utils/utils.jsx'); @@ -10,14 +11,14 @@ var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; function getSearchTermStateFromStores() { - term = PostStore.getSearchTerm(); - if (!term) term = ""; + var term = PostStore.getSearchTerm() || ''; return { search_term: term }; } module.exports = React.createClass({ + displayName: 'SearchBar', componentDidMount: function() { PostStore.addSearchTermChangeListener(this._onChange); }, @@ -58,14 +59,14 @@ module.exports = React.createClass({ e.target.select(); }, performSearch: function(terms, isMentionSearch) { - if (terms.length > 0) { - $("#search-spinner").removeClass("hidden"); + if (terms.length) { + this.setState({isSearching: true}); client.search( terms, function(data) { - $("#search-spinner").addClass("hidden"); - if(utils.isMobile()) { - $('#search')[0].value = ""; + this.setState({isSearching: false}); + if (utils.isMobile()) { + React.findDOMNode(this.refs.search).value = ''; } AppDispatcher.handleServerAction({ @@ -73,18 +74,17 @@ module.exports = React.createClass({ results: data, is_mention_search: isMentionSearch }); - }, + }.bind(this), function(err) { - $("#search-spinner").addClass("hidden"); - dispatchError(err, "search"); - } + this.setState({isSearching: false}); + AsyncClient.dispatchError(err, "search"); + }.bind(this) ); } }, handleSubmit: function(e) { e.preventDefault(); - terms = this.state.search_term.trim(); - this.performSearch(terms); + this.performSearch(this.state.search_term.trim()); }, getInitialState: function() { return getSearchTermStateFromStores(); @@ -95,8 +95,15 @@ module.exports = React.createClass({
      - - + + {this.state.isSearching ? : null}
      ); diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx index 156cf0120e..1fd974642f 100644 --- a/web/react/components/search_results.jsx +++ b/web/react/components/search_results.jsx @@ -8,12 +8,13 @@ var UserStore = require('../stores/user_store.jsx'); var UserProfile = require( './user_profile.jsx' ); var SearchBox =require('./search_bar.jsx'); var utils = require('../utils/utils.jsx'); -var client =require('../utils/client.jsx'); +var client = require('../utils/client.jsx'); +var AsyncClient = require('../utils/async_client.jsx'); var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; -RhsHeaderSearch = React.createClass({ +var RhsHeaderSearch = React.createClass({ handleClose: function(e) { e.preventDefault(); @@ -32,13 +33,13 @@ RhsHeaderSearch = React.createClass({ return (
      {title} - +
      ); } }); -SearchItem = React.createClass({ +var SearchItem = React.createClass({ handleClick: function(e) { e.preventDefault(); @@ -62,15 +63,16 @@ SearchItem = React.createClass({ }); }, function(err) { - dispatchError(err, "getPost"); + AsyncClient.dispatchError(err, "getPost"); } ); - var postChannel = ChannelStore.get(this.props.post.channel_id); - var teammate = postChannel.type === 'D' ? utils.getDirectTeammate(this.props.post.channel_id).username : ""; + var postChannel = ChannelStore.get(this.props.post.channel_id); + var teammate = postChannel.type === 'D' ? utils.getDirectTeammate(this.props.post.channel_id).username : ""; - utils.switchChannel(postChannel,teammate); + utils.switchChannel(postChannel, teammate); }, + render: function() { var message = utils.textToJsx(this.props.post.message, {searchTerm: this.props.term, noMentionHighlight: !this.props.isMentionSearch}); @@ -79,14 +81,10 @@ SearchItem = React.createClass({ var timestamp = UserStore.getCurrentUser().update_at; if (channel) { - if (channel.type === 'D') { - channelName = "Private Message"; - } else { - channelName = channel.display_name; - } + channelName = (channel.type === 'D') ? "Private Message" : channel.display_name; } - return ( + return (
      { channelName }
      @@ -95,7 +93,11 @@ SearchItem = React.createClass({
      • -
      • +
      • + +
      {message}
      @@ -104,11 +106,13 @@ SearchItem = React.createClass({ } }); + function getStateFromStores() { return { results: PostStore.getSearchResults() }; } module.exports = React.createClass({ + displayName: 'SearchResults', componentDidMount: function() { PostStore.addSearchChangeListener(this._onChange); this.resize(); @@ -144,41 +148,24 @@ module.exports = React.createClass({ var results = this.state.results; var currentId = UserStore.getCurrentId(); - var searchForm = currentId == null ? null : ; + var searchForm = currentId ? : null; + var noResults = (!results || !results.order || !results.order.length); + var searchTerm = PostStore.getSearchTerm(); - if (results == null) { - return ( -
      -
      Search Results
      -
      - ); - } - - if (!results.order || results.order.length == 0) { - return ( -
      -
      {searchForm}
      -
      - -
      -
      No results
      -
      -
      -
      - ); - } - - var self = this; return (
      {searchForm}
      - {results.order.map(function(id) { - var post = results.posts[id]; - return - })} + + { noResults ?
      No results
      + : results.order.map(function(id) { + var post = results.posts[id]; + return + }, this) + } +
      diff --git a/web/sass-files/sass/partials/_base.scss b/web/sass-files/sass/partials/_base.scss index fd6225bddc..8f4ff7b60e 100644 --- a/web/sass-files/sass/partials/_base.scss +++ b/web/sass-files/sass/partials/_base.scss @@ -126,6 +126,10 @@ div.theme { to { transform: scale(1) rotate(360deg);} } +.glyphicon-refresh-animate { + @include animation(spin .7s infinite linear); +} + .black-bg { background-color: black !important; } diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss index 9c0c09ee3b..e017392406 100644 --- a/web/sass-files/sass/partials/_responsive.scss +++ b/web/sass-files/sass/partials/_responsive.scss @@ -436,10 +436,9 @@ .form-control { background: none; color: #fff; - border-bottom: 1px solid #fff; border-bottom: 1px solid rgba(#fff, 0.7); border-radius: 0; - padding: 0 0 0 23px; + padding: 0 10px 0 23px; } ::-webkit-input-placeholder { color: #fff; @@ -534,6 +533,11 @@ .sidebar--right__close { display: none; } + .search__form { + .glyphicon { + color: #fff; + } + } } .inner__wrap { &.move--right { diff --git a/web/sass-files/sass/partials/_variables.scss b/web/sass-files/sass/partials/_variables.scss index eb1f3eef31..5d883ab448 100644 --- a/web/sass-files/sass/partials/_variables.scss +++ b/web/sass-files/sass/partials/_variables.scss @@ -7,10 +7,4 @@ $primary-color: #2389D7; $primary-color--hover: darken(#2389D7, 5%); $body-bg: #e9e9e9; $header-bg: #f9f9f9; -$border-gray: 1px solid #ddd; - -// Animation -.glyphicon-refresh-animate { - -animation: spin .7s infinite linear; - -webkit-animation: spin2 .7s infinite linear; -} \ No newline at end of file +$border-gray: 1px solid #ddd; \ No newline at end of file From 0a166572c12e3e13fefee02e79ab9eca6f4d2229 Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Tue, 14 Jul 2015 02:30:55 +0500 Subject: [PATCH 18/90] Merge branch 'MM-1302' of https://github.com/nickago/platform into mm-1302 # Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit. --- web/react/components/sidebar_header.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx index 0b59d20364..7f8fe3d527 100644 --- a/web/react/components/sidebar_header.jsx +++ b/web/react/components/sidebar_header.jsx @@ -121,9 +121,12 @@ module.exports = React.createClass({ }, render: function() { var teamName = this.props.teamName ? this.props.teamName : config.SiteName; + var me = UserStore.getCurrentUser() return (
      + {me.username} { teamName }
      From 3d56d9b2411fdf6db6228dee5dab3fad377a31c4 Mon Sep 17 00:00:00 2001 From: nickago Date: Mon, 13 Jul 2015 14:56:54 -0700 Subject: [PATCH 19/90] Add processing for messageless image posts w/ pluralization --- web/react/components/post_body.jsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx index 7d5ef4d338..2febcfa048 100644 --- a/web/react/components/post_body.jsx +++ b/web/react/components/post_body.jsx @@ -71,11 +71,22 @@ module.exports = React.createClass({ name = {profile.username}; } - var message = parentPost.message; + var message = "" + if(parentPost.message) { + message = utils.replaceHtmlEntities(parentPost.message) + } else if (parentPost.filenames.length) { + message = 2) { + message += " plus " + (parentPost.filenames.length - 1) + " other files"; + } + } comment = (

      - Commented on {name}{apostrophe} message: {utils.replaceHtmlEntities(message)} + Commented on {name}{apostrophe} message: {message}

      ); From d1cb7d35fb178916c9aecef8ecc703f2a364197b Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Tue, 14 Jul 2015 03:26:04 +0500 Subject: [PATCH 20/90] mm-1302 - Adding profile pick in LHS Header --- web/react/components/sidebar_header.jsx | 10 ++++---- web/sass-files/sass/partials/_headers.scss | 27 ++++++++++++++++++---- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx index 7f8fe3d527..54858a04df 100644 --- a/web/react/components/sidebar_header.jsx +++ b/web/react/components/sidebar_header.jsx @@ -77,7 +77,7 @@ var NavbarDropdown = React.createClass({ for (var i = 0; i < this.state.teams.length; i++) { var domain = this.state.teams[i]; - if (domain == utils.getSubDomain()) + if (domain == utils.getSubDomain()) continue; if (teams.length == 0) @@ -125,9 +125,11 @@ module.exports = React.createClass({ return (
      - {me.username} - { teamName } + +
      @{me.username}
      + { teamName } +
      ); diff --git a/web/sass-files/sass/partials/_headers.scss b/web/sass-files/sass/partials/_headers.scss index 1ec1109a59..7b0f24abf9 100644 --- a/web/sass-files/sass/partials/_headers.scss +++ b/web/sass-files/sass/partials/_headers.scss @@ -75,14 +75,16 @@ // Team Header in Sidebar .sidebar--left, .sidebar--menu { .team__header { - padding: 0 15px 0 15px; + padding: 15px; @include legacy-pie-clearfix; a { color: #fff; } .navbar-right { font-size: 0.85em; - margin: 16px -5px 0; + position: absolute; + top: 24px; + right: 25px; .dropdown-toggle { padding: 0 10px; } @@ -100,17 +102,32 @@ display: inline-block; } } - .team__name { + .user__picture { + width: 36px; + height: 36px; float: left; - line-height: 50px; + @include border-radius(36px); + } + .header__info { + padding-left: 42px; + color: #fff; + } + .team__name, .user__name { + display: block; + line-height: 18px; font-weight: 600; - font-size: 1.2em; + font-size: 16px; max-width: 80%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; text-decoration: none; } + .user__name { + font-size: 14px; + font-weight: 400; + color: #eee; + } > .nav { > li { > a { From b31327f072aa575d5ff97cc5e0786e50510ab456 Mon Sep 17 00:00:00 2001 From: it33 Date: Mon, 13 Jul 2015 23:40:40 -0700 Subject: [PATCH 21/90] Updating README_DEV.md with corrected forum links --- scripts/README_DEV.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/README_DEV.md b/scripts/README_DEV.md index 6a2dfc54da..be24daad90 100644 --- a/scripts/README_DEV.md +++ b/scripts/README_DEV.md @@ -10,7 +10,7 @@ DOCKER SETUP 3. Add a line to your /etc/hosts that goes ` dockerhost` 4. Run `boot2docker shellinit` and copy the export statements to your ~/.bash_profile -Any issues? Please let us know on our forums at: http://bit.ly/1MY1kul +Any issues? Please let us know on our forums at: http://forum.mattermost.org GO SETUP @@ -39,4 +39,4 @@ MATTERMOST SETUP 6. Then do `cd platform` and `make test`. Provided the test runs fine, you now have a complete build environment. 7. Use `make run` to run your code -Any issues? Please let us know on our forums at: http://bit.ly/1MY1kul \ No newline at end of file +Any issues? Please let us know on our forums at: http://forum.mattermost.org From d9afa47ef803f3069427192acf839d21bb5a8d93 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Tue, 14 Jul 2015 08:07:01 -0400 Subject: [PATCH 22/90] Revert "MM-831 Time change ping riding on the status interupt for timestamp update" --- web/react/components/post_list.jsx | 16 +--------------- web/react/components/post_right.jsx | 18 ++---------------- 2 files changed, 3 insertions(+), 31 deletions(-) diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index e73bc10cc1..9349d02405 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -35,8 +35,6 @@ module.exports = React.createClass({ oldScrollHeight: 0, oldZoom: 0, scrolledToNew: false, - preForcePosision: 0, - wasForced: false, componentDidMount: function() { var user = UserStore.getCurrentUser(); if (user.props && user.props.theme) { @@ -52,7 +50,6 @@ module.exports = React.createClass({ PostStore.addChangeListener(this._onChange); ChannelStore.addChangeListener(this._onChange); - UserStore.addStatusesChangeListener(this._onTimeChange); SocketStore.addChangeListener(this._onSocketChange); $(".post-list-holder-by-time").perfectScrollbar(); @@ -82,11 +79,6 @@ module.exports = React.createClass({ }); $(post_holder).scroll(function(e){ - if (self.wasForced) { - $(post_holder).scrollTop(self.preForcePosision); - $(post_holder).perfectScrollbar('update'); - self.wasForced = false; - } if (!self.preventScrollTrigger) { self.scrollPosition = $(post_holder).scrollTop() + $(post_holder).innerHeight(); } @@ -137,7 +129,6 @@ module.exports = React.createClass({ componentWillUnmount: function() { PostStore.removeChangeListener(this._onChange); ChannelStore.removeChangeListener(this._onChange); - UserStore.removeStatusesChangeListener(this._onTimeChange); SocketStore.removeChangeListener(this._onSocketChange); $('body').off('click.userpopover'); }, @@ -170,7 +161,7 @@ module.exports = React.createClass({ this.scrolledToNew = false; } this.setState(newState); - } + } }, _onSocketChange: function(msg) { if (msg.action == "posted") { @@ -234,11 +225,6 @@ module.exports = React.createClass({ AsyncClient.getProfiles(); } }, - _onTimeChange: function() { - this.wasForced = true; - this.preForcePosision = $(".post-list-holder-by-time").scrollTop(); - this.forceUpdate(); - }, getMorePosts: function(e) { e.preventDefault(); diff --git a/web/react/components/post_right.jsx b/web/react/components/post_right.jsx index 27195dfbab..408fbf83ac 100644 --- a/web/react/components/post_right.jsx +++ b/web/react/components/post_right.jsx @@ -281,11 +281,9 @@ function getStateFromStores() { } module.exports = React.createClass({ - wasForced: false, componentDidMount: function() { PostStore.addSelectedPostChangeListener(this._onChange); PostStore.addChangeListener(this._onChangeAll); - UserStore.addStatusesChangeListener(this._onTimeChange); this.resize(); var self = this; $(window).resize(function(){ @@ -293,15 +291,11 @@ module.exports = React.createClass({ }); }, componentDidUpdate: function() { - if(!this.wasForced){ - this.resize(); - this.wasForced = false; - } + this.resize(); }, componentWillUnmount: function() { PostStore.removeSelectedPostChangeListener(this._onChange); PostStore.removeChangeListener(this._onChangeAll); - UserStore.removeStatusesChangeListener(this._onTimeChange); }, _onChange: function() { if (this.isMounted()) { @@ -341,14 +335,6 @@ module.exports = React.createClass({ this.setState(getStateFromStores()); } }, - _onTimeChange: function() { - this.wasForced = true; - for (var key in this.refs) { - if(this.refs[key].forceUpdate != undefined) { - this.refs[key].forceUpdate(); - } - } - }, getInitialState: function() { return getStateFromStores(); }, @@ -408,7 +394,7 @@ module.exports = React.createClass({
      { posts_array.map(function(cpost) { - return + return })}
      From 634df1c6242772e44fed5f64ac238adf2d0503a0 Mon Sep 17 00:00:00 2001 From: nickago Date: Tue, 14 Jul 2015 07:54:09 -0700 Subject: [PATCH 23/90] Added semicolons --- web/react/components/post_list.jsx | 2 +- web/react/components/post_right.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index e186fb7065..99b1b514a1 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -269,7 +269,7 @@ module.exports = React.createClass({ _onTimeChange: function() { this.wasForced = true; this.preForcePosision = $(".post-list-holder-by-time").scrollTop(); - this.forceUpdate() + this.forceUpdate(); }, getMorePosts: function(e) { e.preventDefault(); diff --git a/web/react/components/post_right.jsx b/web/react/components/post_right.jsx index 0c37545291..ab818c1bdf 100644 --- a/web/react/components/post_right.jsx +++ b/web/react/components/post_right.jsx @@ -299,7 +299,7 @@ module.exports = React.createClass({ componentWillUnmount: function() { PostStore.removeSelectedPostChangeListener(this._onChange); PostStore.removeChangeListener(this._onChangeAll); - UserStore.removeStatusesChangeListener(this._onTimeChange) + UserStore.removeStatusesChangeListener(this._onTimeChange); }, _onChange: function() { if (this.isMounted()) { From 7ff57181b9aa182a7073a923004d63bbbe100e1d Mon Sep 17 00:00:00 2001 From: hmhealey Date: Tue, 14 Jul 2015 11:48:53 -0400 Subject: [PATCH 24/90] Changed image resizing for both the preview and thumbnail to use Lanczos interpolation instead of simple nearest neighbour --- api/file.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/file.go b/api/file.go index 0e08567d65..eaba515a47 100644 --- a/api/file.go +++ b/api/file.go @@ -13,9 +13,9 @@ import ( "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" "github.com/nfnt/resize" + _ "golang.org/x/image/bmp" "image" _ "image/gif" - _ "golang.org/x/image/bmp" "image/jpeg" "io" "net/http" @@ -157,7 +157,7 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch go func() { var thumbnail image.Image if imgConfig.Width > int(utils.Cfg.ImageSettings.ThumbnailWidth) { - thumbnail = resize.Resize(utils.Cfg.ImageSettings.ThumbnailWidth, utils.Cfg.ImageSettings.ThumbnailHeight, img, resize.NearestNeighbor) + thumbnail = resize.Resize(utils.Cfg.ImageSettings.ThumbnailWidth, utils.Cfg.ImageSettings.ThumbnailHeight, img, resize.Lanczos3) } else { thumbnail = img } @@ -182,7 +182,7 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch go func() { var preview image.Image if imgConfig.Width > int(utils.Cfg.ImageSettings.PreviewWidth) { - preview = resize.Resize(utils.Cfg.ImageSettings.PreviewWidth, utils.Cfg.ImageSettings.PreviewHeight, img, resize.NearestNeighbor) + preview = resize.Resize(utils.Cfg.ImageSettings.PreviewWidth, utils.Cfg.ImageSettings.PreviewHeight, img, resize.Lanczos3) } else { preview = img } From 9f816e25545b7487d3078813ffd0e120a3a100cd Mon Sep 17 00:00:00 2001 From: nickago Date: Tue, 14 Jul 2015 11:55:49 -0700 Subject: [PATCH 25/90] Need you GDev key for youtube stuff --- web/react/utils/utils.jsx | 23 +++++++++++++++-------- web/static/config/config.js | 4 ++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 5ded0e76f5..f3fe3a9928 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -303,18 +303,25 @@ var getYoutubeEmbed = function(link) { }; var success = function(data) { - $('.video-uploader.'+youtubeId).html(data.data.uploader); - $('.video-title.'+youtubeId).find('a').html(data.data.title); + if(!data.items.length || !data.items[0].snippet) { + return; + } + metadata = data.items[0].snippet; + $('.video-uploader.'+youtubeId).html(metadata.channelTitle); + $('.video-title.'+youtubeId).find('a').html(metadata.title); $(".post-list-holder-by-time").scrollTop($(".post-list-holder-by-time")[0].scrollHeight); $(".post-list-holder-by-time").perfectScrollbar('update'); }; - $.ajax({ - async: true, - url: 'https://gdata.youtube.com/feeds/api/videos/'+youtubeId+'?v=2&alt=jsonc', - type: 'GET', - success: success - }); + if(config.GoogleDeveloperKey) { + $.ajax({ + async: true, + url: "https://www.googleapis.com/youtube/v3/videos", + type: 'GET', + data: {part:"snippet", id:youtubeId, key:config.GoogleDeveloperKey}, + success: success + }); + } return (
      diff --git a/web/static/config/config.js b/web/static/config/config.js index 45c713da2e..0d564b77ed 100644 --- a/web/static/config/config.js +++ b/web/static/config/config.js @@ -16,6 +16,10 @@ var config = { RequireInviteNames: false, AllowSignupDomainsWizard: false, + // Google Developer Key (for Youtube API links) + // Leave blank to disable + GoogleDeveloperKey: "", + // Privacy switches ShowEmail: true, From fa4ac0c7890aa148122407feb2575470336099e1 Mon Sep 17 00:00:00 2001 From: nickago Date: Tue, 14 Jul 2015 12:08:00 -0700 Subject: [PATCH 26/90] Fixed minor typo --- web/react/components/post_body.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx index 2febcfa048..5f7dbe6c96 100644 --- a/web/react/components/post_body.jsx +++ b/web/react/components/post_body.jsx @@ -75,7 +75,7 @@ module.exports = React.createClass({ if(parentPost.message) { message = utils.replaceHtmlEntities(parentPost.message) } else if (parentPost.filenames.length) { - message = Date: Tue, 14 Jul 2015 13:09:14 -0700 Subject: [PATCH 27/90] Team admin can now delete any post --- api/post.go | 21 ++++++++++++++------- api/post_test.go | 14 +++++++++++++- web/react/components/post_info.jsx | 3 ++- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/api/post.go b/api/post.go index 650f47062d..0a8b5a20bb 100644 --- a/api/post.go +++ b/api/post.go @@ -619,16 +619,23 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId) pchan := Srv.Store.Post().Get(postId) + uchan := Srv.Store.User().Get(c.Session.UserId) - if !c.HasPermissionsToChannel(cchan, "deletePost") { + if uresult := <-uchan; uresult.Err != nil { + c.Err = uresult.Err return - } - - if result := <-pchan; result.Err != nil { - c.Err = result.Err + } else if presult := <-pchan; presult.Err != nil { + c.Err = presult.Err return } else { - post := result.Data.(*model.PostList).Posts[postId] + + user := uresult.Data.(*model.User) + + if !c.HasPermissionsToChannel(cchan, "deletePost") && !strings.Contains(user.Roles,"admin"){ + return + } + + post := presult.Data.(*model.PostList).Posts[postId] if post == nil { c.SetInvalidParam("deletePost", "postId") @@ -641,7 +648,7 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { return } - if post.UserId != c.Session.UserId { + if post.UserId != c.Session.UserId && !strings.Contains(user.Roles,"admin") { c.Err = model.NewAppError("deletePost", "You do not have the appropriate permissions", "") c.Err.StatusCode = http.StatusForbidden return diff --git a/api/post_test.go b/api/post_test.go index 970307759d..5009ff54d1 100644 --- a/api/post_test.go +++ b/api/post_test.go @@ -483,6 +483,10 @@ func TestDeletePosts(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) + userAdmin := &model.User{TeamId: team.Id, Email: team.Email, FullName: "Corey Hulen", Password: "pwd"} + userAdmin = Client.Must(Client.CreateUser(userAdmin, "")).Data.(*model.User) + store.Must(Srv.Store.User().VerifyEmail(userAdmin.Id)) + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) @@ -521,8 +525,16 @@ func TestDeletePosts(t *testing.T) { r2 := Client.Must(Client.GetPosts(channel1.Id, 0, 10, "")).Data.(*model.PostList) if len(r2.Posts) != 4 { - t.Fatal("should have returned 5 items") + t.Fatal("should have returned 4 items") } + + time.Sleep(10 * time.Millisecond) + post4 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} + post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post) + + Client.LoginByEmail(team.Domain, userAdmin.Email, "pwd") + + Client.Must(Client.DeletePost(channel1.Id, post4.Id)) } func TestEmailMention(t *testing.T) { diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx index cf01747f00..48efa95ba9 100644 --- a/web/react/components/post_info.jsx +++ b/web/react/components/post_info.jsx @@ -11,6 +11,7 @@ module.exports = React.createClass({ render: function() { var post = this.props.post; var isOwner = UserStore.getCurrentId() == post.user_id; + var isAdmin = UserStore.getCurrentUser().roles.indexOf("admin") > -1 var type = "Post" if (post.root_id.length > 0) { @@ -36,7 +37,7 @@ module.exports = React.createClass({
        { isOwner ?
      • Edit
      • : "" } - { isOwner ?
      • Delete
      • + { isOwner || isAdmin ?
      • Delete
      • : "" } { this.props.allowReply === "true" ?
      • Reply
      • : "" } From 246d12aaf23fa3a2c23225b33a333effff76253b Mon Sep 17 00:00:00 2001 From: =Corey Hulen Date: Tue, 14 Jul 2015 15:12:04 -0800 Subject: [PATCH 28/90] fixes mm-1348 removing dependency on redis --- api/channel.go | 5 ++- api/command.go | 2 -- api/post.go | 8 ++--- api/server.go | 3 +- api/user.go | 2 +- api/web_conn.go | 2 +- api/web_hub.go | 40 ++++++++++++++------- api/web_socket_test.go | 3 -- api/web_team_hub.go | 40 --------------------- config/config.json | 4 --- config/config_docker.json | 4 --- store/redis.go | 75 --------------------------------------- store/redis_test.go | 59 ------------------------------ utils/config.go | 6 ---- 14 files changed, 36 insertions(+), 217 deletions(-) delete mode 100644 store/redis.go delete mode 100644 store/redis_test.go diff --git a/api/channel.go b/api/channel.go index 88db27def1..4d8dbad099 100644 --- a/api/channel.go +++ b/api/channel.go @@ -8,7 +8,6 @@ import ( "fmt" "github.com/gorilla/mux" "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" "net/http" "strings" ) @@ -542,7 +541,7 @@ func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) { message := model.NewMessage(c.Session.TeamId, id, c.Session.UserId, model.ACTION_VIEWED) message.Add("channel_id", id) - store.PublishAndForget(message) + PublishAndForget(message) result := make(map[string]string) result["id"] = id @@ -657,7 +656,7 @@ func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) { message := model.NewMessage(c.Session.TeamId, "", userId, model.ACTION_USER_ADDED) - store.PublishAndForget(message) + PublishAndForget(message) <-Srv.Store.Channel().UpdateLastViewedAt(id, oUser.Id) w.Write([]byte(cm.ToJson())) diff --git a/api/command.go b/api/command.go index 810a8a07ed..ee7a11af35 100644 --- a/api/command.go +++ b/api/command.go @@ -27,8 +27,6 @@ var commands = []commandHandler{ func InitCommand(r *mux.Router) { l4g.Debug("Initializing command api routes") r.Handle("/command", ApiUserRequired(command)).Methods("POST") - - hub.Start() } func command(c *Context, w http.ResponseWriter, r *http.Request) { diff --git a/api/post.go b/api/post.go index 02f997166a..aa9b132928 100644 --- a/api/post.go +++ b/api/post.go @@ -11,10 +11,10 @@ import ( "github.com/mattermost/platform/store" "github.com/mattermost/platform/utils" "net/http" + "path/filepath" "strconv" "strings" "time" - "path/filepath" ) func InitPost(r *mux.Router) { @@ -455,7 +455,7 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) { message.Add("mentions", model.ArrayToJson(mentionedUsers)) } - store.PublishAndForget(message) + PublishAndForget(message) }() } @@ -521,7 +521,7 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { message.Add("channel_id", rpost.ChannelId) message.Add("message", rpost.Message) - store.PublishAndForget(message) + PublishAndForget(message) w.Write([]byte(rpost.ToJson())) } @@ -670,7 +670,7 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { message.Add("post_id", post.Id) message.Add("channel_id", post.ChannelId) - store.PublishAndForget(message) + PublishAndForget(message) result := make(map[string]string) result["id"] = postId diff --git a/api/server.go b/api/server.go index 58986a8d41..3163f79f53 100644 --- a/api/server.go +++ b/api/server.go @@ -28,7 +28,6 @@ func NewServer() { Srv = &Server{} Srv.Server = manners.NewServer() Srv.Store = store.NewSqlStore() - store.RedisClient() Srv.Router = mux.NewRouter() Srv.Router.NotFoundHandler = http.HandlerFunc(Handle404) @@ -54,7 +53,7 @@ func StopServer() { Srv.Server.Shutdown <- true Srv.Store.Close() - store.RedisClose() + hub.Stop() l4g.Info("Server stopped") } diff --git a/api/user.go b/api/user.go index 5b052e826c..df1f450424 100644 --- a/api/user.go +++ b/api/user.go @@ -196,7 +196,7 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User { // This message goes to every channel, so the channelId is irrelevant message := model.NewMessage(team.Id, "", ruser.Id, model.ACTION_NEW_USER) - store.PublishAndForget(message) + PublishAndForget(message) return ruser } diff --git a/api/web_conn.go b/api/web_conn.go index 751f6f407f..0990de8ef2 100644 --- a/api/web_conn.go +++ b/api/web_conn.go @@ -70,7 +70,7 @@ func (c *WebConn) readPump() { } else { msg.TeamId = c.TeamId msg.UserId = c.UserId - store.PublishAndForget(&msg) + PublishAndForget(&msg) } } } diff --git a/api/web_hub.go b/api/web_hub.go index bf5fbb3216..c7be19cacb 100644 --- a/api/web_hub.go +++ b/api/web_hub.go @@ -5,12 +5,14 @@ package api import ( l4g "code.google.com/p/log4go" + "github.com/mattermost/platform/model" ) type Hub struct { teamHubs map[string]*TeamHub register chan *WebConn unregister chan *WebConn + broadcast chan *model.Message stop chan string } @@ -18,9 +20,16 @@ var hub = &Hub{ register: make(chan *WebConn), unregister: make(chan *WebConn), teamHubs: make(map[string]*TeamHub), + broadcast: make(chan *model.Message), stop: make(chan string), } +func PublishAndForget(message *model.Message) { + go func() { + hub.Broadcast(message) + }() +} + func (h *Hub) Register(webConn *WebConn) { h.register <- webConn } @@ -29,8 +38,14 @@ func (h *Hub) Unregister(webConn *WebConn) { h.unregister <- webConn } -func (h *Hub) Stop(teamId string) { - h.stop <- teamId +func (h *Hub) Broadcast(message *model.Message) { + if message != nil { + h.broadcast <- message + } +} + +func (h *Hub) Stop() { + h.stop <- "all" } func (h *Hub) Start() { @@ -53,18 +68,17 @@ func (h *Hub) Start() { if nh, ok := h.teamHubs[c.TeamId]; ok { nh.Unregister(c) } - - case s := <-h.stop: - if len(s) == 0 { - l4g.Debug("stopping all connections") - for _, v := range h.teamHubs { - v.Stop() - } - return - } else if nh, ok := h.teamHubs[s]; ok { - delete(h.teamHubs, s) - nh.Stop() + case msg := <-h.broadcast: + nh := h.teamHubs[msg.TeamId] + if nh != nil { + nh.broadcast <- msg } + case s := <-h.stop: + l4g.Debug("stopping %v connections", s) + for _, v := range h.teamHubs { + v.Stop() + } + return } } }() diff --git a/api/web_socket_test.go b/api/web_socket_test.go index 4cb49220f9..6f6a7d6195 100644 --- a/api/web_socket_test.go +++ b/api/web_socket_test.go @@ -115,9 +115,6 @@ func TestSocket(t *testing.T) { }() time.Sleep(2 * time.Second) - - hub.Stop(team.Id) - } func TestZZWebSocketTearDown(t *testing.T) { diff --git a/api/web_team_hub.go b/api/web_team_hub.go index 7c7981e76f..7a63b84d10 100644 --- a/api/web_team_hub.go +++ b/api/web_team_hub.go @@ -6,8 +6,6 @@ package api import ( l4g "code.google.com/p/log4go" "github.com/mattermost/platform/model" - "github.com/mattermost/platform/store" - "strings" ) type TeamHub struct { @@ -43,43 +41,6 @@ func (h *TeamHub) Stop() { } func (h *TeamHub) Start() { - - pubsub := store.RedisClient().PubSub() - - go func() { - defer func() { - l4g.Debug("redis reader finished for teamId=%v", h.teamId) - hub.Stop(h.teamId) - }() - - l4g.Debug("redis reader starting for teamId=%v", h.teamId) - - err := pubsub.Subscribe(h.teamId) - if err != nil { - l4g.Error("Error while subscribing to redis %v %v", h.teamId, err) - return - } - - for { - if payload, err := pubsub.ReceiveTimeout(REDIS_WAIT); err != nil { - if strings.Contains(err.Error(), "i/o timeout") { - if len(h.connections) == 0 { - l4g.Debug("No active connections so sending stop %v", h.teamId) - return - } - } else { - return - } - } else { - msg := store.GetMessageFromPayload(payload) - if msg != nil { - h.broadcast <- msg - } - } - } - - }() - go func() { for { select { @@ -110,7 +71,6 @@ func (h *TeamHub) Start() { webCon.WebSocket.Close() } - pubsub.Close() return } } diff --git a/config/config.json b/config/config.json index b0a019e8de..dfbddaf9f6 100644 --- a/config/config.json +++ b/config/config.json @@ -31,10 +31,6 @@ "Trace": false, "AtRestEncryptKey": "Ya0xMrybACJ3sZZVWQC7e31h5nSDWZFS" }, - "RedisSettings": { - "DataSource": "dockerhost:6379", - "MaxOpenConns": 1000 - }, "AWSSettings": { "S3AccessKeyId": "", "S3SecretAccessKey": "", diff --git a/config/config_docker.json b/config/config_docker.json index 85f0d9c73b..dd7ec23ffe 100644 --- a/config/config_docker.json +++ b/config/config_docker.json @@ -31,10 +31,6 @@ "Trace": false, "AtRestEncryptKey": "Ya0xMrybACJ3sZZVWQC7e31h5nSDWZFS" }, - "RedisSettings": { - "DataSource": "localhost:6379", - "MaxOpenConns": 1000 - }, "AWSSettings": { "S3AccessKeyId": "", "S3SecretAccessKey": "", diff --git a/store/redis.go b/store/redis.go deleted file mode 100644 index 262040d437..0000000000 --- a/store/redis.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. -// See License.txt for license information. - -package store - -import ( - l4g "code.google.com/p/log4go" - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/utils" - "gopkg.in/redis.v2" - "strings" - "time" -) - -var client *redis.Client - -func RedisClient() *redis.Client { - - if client == nil { - - addr := utils.Cfg.RedisSettings.DataSource - - client = redis.NewTCPClient(&redis.Options{ - Addr: addr, - Password: "", - DB: 0, - PoolSize: utils.Cfg.RedisSettings.MaxOpenConns, - }) - - l4g.Info("Pinging redis at '%v'", addr) - pong, err := client.Ping().Result() - - if err != nil { - l4g.Critical("Failed to open redis connection to '%v' err:%v", addr, err) - time.Sleep(time.Second) - panic("Failed to open redis connection " + err.Error()) - } - - if pong != "PONG" { - l4g.Critical("Failed to ping redis connection to '%v' err:%v", addr, err) - time.Sleep(time.Second) - panic("Failed to open ping connection " + err.Error()) - } - } - - return client -} - -func RedisClose() { - l4g.Info("Closing redis") - - if client != nil { - client.Close() - client = nil - } -} - -func PublishAndForget(message *model.Message) { - - go func() { - c := RedisClient() - result := c.Publish(message.TeamId, message.ToJson()) - if result.Err() != nil { - l4g.Error("Failed to publish message err=%v, payload=%v", result.Err(), message.ToJson()) - } - }() -} - -func GetMessageFromPayload(m interface{}) *model.Message { - if msg, found := m.(*redis.Message); found { - return model.MessageFromJson(strings.NewReader(msg.Payload)) - } else { - return nil - } -} diff --git a/store/redis_test.go b/store/redis_test.go deleted file mode 100644 index 11bd9ca6a0..0000000000 --- a/store/redis_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. -// See License.txt for license information. - -package store - -import ( - "fmt" - "github.com/mattermost/platform/model" - "github.com/mattermost/platform/utils" - "testing" -) - -func TestRedis(t *testing.T) { - utils.LoadConfig("config.json") - - c := RedisClient() - - if c == nil { - t.Fatal("should have a valid redis connection") - } - - pubsub := c.PubSub() - defer pubsub.Close() - - m := model.NewMessage(model.NewId(), model.NewId(), model.NewId(), model.ACTION_TYPING) - m.Add("RootId", model.NewId()) - - err := pubsub.Subscribe(m.TeamId) - if err != nil { - t.Fatal(err) - } - - // should be the subscribe success message - // lets gobble that up - if _, err := pubsub.Receive(); err != nil { - t.Fatal(err) - } - - PublishAndForget(m) - - fmt.Println("here1") - - if msg, err := pubsub.Receive(); err != nil { - t.Fatal(err) - } else { - - rmsg := GetMessageFromPayload(msg) - - if m.TeamId != rmsg.TeamId { - t.Fatal("Ids do not match") - } - - if m.Props["RootId"] != rmsg.Props["RootId"] { - t.Fatal("Ids do not match") - } - } - - RedisClose() -} diff --git a/utils/config.go b/utils/config.go index eb2ae30505..a166c3d0ee 100644 --- a/utils/config.go +++ b/utils/config.go @@ -42,11 +42,6 @@ type SqlSettings struct { AtRestEncryptKey string } -type RedisSettings struct { - DataSource string - MaxOpenConns int -} - type LogSettings struct { ConsoleEnable bool ConsoleLevel string @@ -112,7 +107,6 @@ type Config struct { LogSettings LogSettings ServiceSettings ServiceSettings SqlSettings SqlSettings - RedisSettings RedisSettings AWSSettings AWSSettings ImageSettings ImageSettings EmailSettings EmailSettings From 46e076529a15da3b5dd63070fbb8e887dba9b436 Mon Sep 17 00:00:00 2001 From: nickago Date: Wed, 15 Jul 2015 08:45:17 -0700 Subject: [PATCH 29/90] Fixed typo, returned password flow to standard model --- web/react/components/user_settings.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx index b4c3747afc..06d8d0208b 100644 --- a/web/react/components/user_settings.jsx +++ b/web/react/components/user_settings.jsx @@ -626,7 +626,7 @@ var SecurityTab = React.createClass({ client.updatePassword(data, function(data) { - this.updateSection(""); + this.props.updateSection(""); AsyncClient.getMe(); this.setState({ current_password: '', new_password: '', confirm_password: '' }); }.bind(this), From c1e23faab71443bb7af6a74cc5705e000b66f53f Mon Sep 17 00:00:00 2001 From: nickago Date: Wed, 15 Jul 2015 09:13:34 -0700 Subject: [PATCH 30/90] Changed scope of 'metatdata' --- web/react/utils/utils.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index f3fe3a9928..b8747b8fc1 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -306,7 +306,7 @@ var getYoutubeEmbed = function(link) { if(!data.items.length || !data.items[0].snippet) { return; } - metadata = data.items[0].snippet; + var metadata = data.items[0].snippet; $('.video-uploader.'+youtubeId).html(metadata.channelTitle); $('.video-title.'+youtubeId).find('a').html(metadata.title); $(".post-list-holder-by-time").scrollTop($(".post-list-holder-by-time")[0].scrollHeight); From 42a213c54d0825e72cf5b85b6c379f20cdce7b75 Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Wed, 15 Jul 2015 22:32:08 +0500 Subject: [PATCH 31/90] Minor UI changes --- web/sass-files/sass/partials/_files.scss | 2 +- web/sass-files/sass/partials/_headers.scss | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web/sass-files/sass/partials/_files.scss b/web/sass-files/sass/partials/_files.scss index 79142176ec..c584c240d1 100644 --- a/web/sass-files/sass/partials/_files.scss +++ b/web/sass-files/sass/partials/_files.scss @@ -137,7 +137,7 @@ border: 1px solid #E2E2E2; background-color: #FFF; background-repeat: no-repeat; - background-position: left center; + background-position: top left; } a { text-decoration: none; diff --git a/web/sass-files/sass/partials/_headers.scss b/web/sass-files/sass/partials/_headers.scss index 7b0f24abf9..338f5ceb46 100644 --- a/web/sass-files/sass/partials/_headers.scss +++ b/web/sass-files/sass/partials/_headers.scss @@ -75,7 +75,7 @@ // Team Header in Sidebar .sidebar--left, .sidebar--menu { .team__header { - padding: 15px; + padding: 10px; @include legacy-pie-clearfix; a { color: #fff; @@ -83,8 +83,8 @@ .navbar-right { font-size: 0.85em; position: absolute; - top: 24px; - right: 25px; + top: 20px; + right: 22px; .dropdown-toggle { padding: 0 10px; } From 288ad55f806995fd3131e6e318729344ef534c87 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Thu, 16 Jul 2015 05:05:26 -0400 Subject: [PATCH 32/90] Revert "MM-1494 Change image resizing for preview and thumbnail to use Lanczos interpolation" --- api/file.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/file.go b/api/file.go index eaba515a47..0e08567d65 100644 --- a/api/file.go +++ b/api/file.go @@ -13,9 +13,9 @@ import ( "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" "github.com/nfnt/resize" - _ "golang.org/x/image/bmp" "image" _ "image/gif" + _ "golang.org/x/image/bmp" "image/jpeg" "io" "net/http" @@ -157,7 +157,7 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch go func() { var thumbnail image.Image if imgConfig.Width > int(utils.Cfg.ImageSettings.ThumbnailWidth) { - thumbnail = resize.Resize(utils.Cfg.ImageSettings.ThumbnailWidth, utils.Cfg.ImageSettings.ThumbnailHeight, img, resize.Lanczos3) + thumbnail = resize.Resize(utils.Cfg.ImageSettings.ThumbnailWidth, utils.Cfg.ImageSettings.ThumbnailHeight, img, resize.NearestNeighbor) } else { thumbnail = img } @@ -182,7 +182,7 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch go func() { var preview image.Image if imgConfig.Width > int(utils.Cfg.ImageSettings.PreviewWidth) { - preview = resize.Resize(utils.Cfg.ImageSettings.PreviewWidth, utils.Cfg.ImageSettings.PreviewHeight, img, resize.Lanczos3) + preview = resize.Resize(utils.Cfg.ImageSettings.PreviewWidth, utils.Cfg.ImageSettings.PreviewHeight, img, resize.NearestNeighbor) } else { preview = img } From 56db6ad08cf23dbdcee326fa39bda8bcdcdecf9e Mon Sep 17 00:00:00 2001 From: ralder Date: Wed, 15 Jul 2015 09:03:28 -0700 Subject: [PATCH 33/90] remove admin's ability to manage members of default channel (fixes #172) --- web/react/components/channel_header.jsx | 33 +++-- web/react/components/navbar.jsx | 167 +++++++++++----------- web/react/components/post_list.jsx | 3 +- web/react/components/sidebar_header.jsx | 48 +++---- web/react/stores/channel_store.jsx | 53 +++---- web/react/stores/user_store.jsx | 2 +- web/sass-files/sass/partials/_navbar.scss | 1 + web/templates/head.html | 3 +- 8 files changed, 144 insertions(+), 166 deletions(-) diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx index fe381a59e9..6791233d5f 100644 --- a/web/react/components/channel_header.jsx +++ b/web/react/components/channel_header.jsx @@ -142,10 +142,10 @@ module.exports = React.createClass({ return null; } - var description = utils.textToJsx(this.state.channel.description, {"singleline": true, "noMentionHighlight": true}); - var popoverContent = React.renderToString(); - var channelTitle = this.state.channel.display_name; - var channelName = this.state.channel.name; + var channel = this.state.channel; + var description = utils.textToJsx(channel.description, {"singleline": true, "noMentionHighlight": true}); + var popoverContent = React.renderToString(); + var channelTitle = channel.display_name; var currentId = UserStore.getCurrentId(); var isAdmin = this.state.memberChannel.roles.indexOf("admin") > -1 || this.state.memberTeam.roles.indexOf("admin") > -1; var isDirect = (this.state.channel.type === 'D'); @@ -169,23 +169,26 @@ module.exports = React.createClass({
          -
        • View Info
        • -
        • Add Members
        • - { isAdmin ? +
        • View Info
        • + { !ChannelStore.isDefault(channel) ? +
        • Add Members
        • + : null + } + { isAdmin && !ChannelStore.isDefault(channel) ?
        • Manage Members
        • : null } -
        • Set Channel Description...
        • -
        • Notification Preferences
        • - { isAdmin && channelName != Constants.DEFAULT_CHANNEL ? -
        • Rename Channel...
        • +
        • Set Channel Description...
        • +
        • Notification Preferences
        • + { isAdmin && !ChannelStore.isDefault(channel) ? +
        • Rename Channel...
        • : null } - { isAdmin && channelName != Constants.DEFAULT_CHANNEL ? -
        • Delete Channel...
        • + { isAdmin && !ChannelStore.isDefault(channel) ? +
        • Delete Channel...
        • : null } - { channelName != Constants.DEFAULT_CHANNEL ? + { !ChannelStore.isDefault(channel) ?
        • Leave Channel
        • : null } @@ -197,7 +200,7 @@ module.exports = React.createClass({ {channelTitle} } - +
          diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx index 35f7d90445..78cf7d8b82 100644 --- a/web/react/components/navbar.jsx +++ b/web/react/components/navbar.jsx @@ -2,21 +2,20 @@ // See License.txt for license information. -var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var utils = require('../utils/utils.jsx'); var client = require('../utils/client.jsx'); var AsyncClient = require('../utils/async_client.jsx'); -var Sidebar = require('./sidebar.jsx'); var UserStore = require('../stores/user_store.jsx'); -var SocketStore = require('../stores/socket_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx'); -var Constants = require('../utils/constants.jsx'); + var UserProfile = require('./user_profile.jsx'); var MessageWrapper = require('./message_wrapper.jsx'); + +var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; +var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); function getCountsStateFromStores() { - var count = 0; var channels = ChannelStore.getAll(); var members = ChannelStore.getAllMembers(); @@ -34,7 +33,7 @@ function getCountsStateFromStores() { } }); - return { count: count } + return { count: count }; } var NotifyCounts = React.createClass({ @@ -54,11 +53,10 @@ var NotifyCounts = React.createClass({ return getCountsStateFromStores(); }, render: function() { - if (this.state.count == 0) { - return (); - } - else { - return ({ this.state.count }); + if (this.state.count) { + return { this.state.count }; + } else { + return null; } } }); @@ -66,25 +64,25 @@ var NotifyCounts = React.createClass({ var NavbarLoginForm = React.createClass({ handleSubmit: function(e) { e.preventDefault(); - var state = { } + var state = { }; var domain = this.refs.domain.getDOMNode().value.trim(); if (!domain) { - state.server_error = "A domain is required" + state.server_error = "A domain is required"; this.setState(state); return; } var email = this.refs.email.getDOMNode().value.trim(); if (!email) { - state.server_error = "An email is required" + state.server_error = "An email is required"; this.setState(state); return; } var password = this.refs.password.getDOMNode().value.trim(); if (!password) { - state.server_error = "A password is required" + state.server_error = "A password is required"; this.setState(state); return; } @@ -105,7 +103,7 @@ var NavbarLoginForm = React.createClass({ window.location.href = '/channels/town-square'; } - }.bind(this), + }, function(err) { if (err.message == "Login failed because email address has not been verified") { window.location.href = '/verify?domain=' + encodeURIComponent(domain) + '&email=' + encodeURIComponent(email); @@ -159,13 +157,14 @@ function getStateFromStores() { } module.exports = React.createClass({ + displayName: 'Navbar', + componentDidMount: function() { ChannelStore.addChangeListener(this._onChange); ChannelStore.addExtraInfoChangeListener(this._onChange); - var self = this; - $('.inner__wrap').click(self.hideSidebars); + $('.inner__wrap').click(this.hideSidebars); - $('body').on('click.infopopover', function(e){ + $('body').on('click.infopopover', function(e) { if ($(e.target).attr('data-toggle') !== 'popover' && $(e.target).parents('.popover.in').length === 0) { $('.info-popover').popover('hide'); @@ -181,13 +180,13 @@ module.exports = React.createClass({ }, handleLeave: function(e) { client.leaveChannel(this.state.channel.id, - function(data) { + function() { AsyncClient.getChannels(true); window.location.href = '/channels/town-square'; - }.bind(this), + }, function(err) { AsyncClient.dispatchError(err, "handleLeave"); - }.bind(this) + } ); }, hideSidebars: function(e) { @@ -204,7 +203,7 @@ module.exports = React.createClass({ }); if (e.target.className != 'navbar-toggle' && e.target.className != 'icon-bar') { - $('.inner__wrap').removeClass('move--right').removeClass('move--left').removeClass('move--left-small'); + $('.inner__wrap').removeClass('move--right move--left move--left-small'); $('.sidebar--left').removeClass('move--right'); $('.sidebar--right').removeClass('move--left'); $('.sidebar--menu').removeClass('move--left'); @@ -229,24 +228,22 @@ module.exports = React.createClass({ render: function() { var currentId = UserStore.getCurrentId(); - var channelName = ""; var popoverContent = ""; var channelTitle = this.props.teamName; var isAdmin = false; var isDirect = false; var description = "" + var channel = this.state.channel; - if (this.state.channel) { - var channel = this.state.channel; - description = utils.textToJsx(this.state.channel.description, {"singleline": true, "noMentionHighlight": true}); - popoverContent = React.renderToString(); - channelName = this.state.channel.name; + if (channel) { + description = utils.textToJsx(channel.description, {"singleline": true, "noMentionHighlight": true}); + popoverContent = React.renderToString(); isAdmin = this.state.member.roles.indexOf("admin") > -1; if (channel.type === 'O') { - channelTitle = this.state.channel.display_name; + channelTitle = channel.display_name; } else if (channel.type === 'P') { - channelTitle = this.state.channel.display_name; + channelTitle = channel.display_name; } else if (channel.type === 'D') { isDirect = true; if (this.state.users.length > 1) { @@ -258,12 +255,11 @@ module.exports = React.createClass({ } } - if(this.state.channel.description.length == 0){ - popoverContent = React.renderToString(
          No channel description yet.
          Click here to add one.
          ); + if (channel.description.length == 0) { + popoverContent = React.renderToString(
          No channel description yet.
          Click here to add one.
          ); } } - var loginForm = currentId == null ? : null; var navbar_collapse_button = currentId != null ? null :
          ); -} + } }); - - diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index 98491976f7..573799a190 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -6,7 +6,6 @@ var ChannelStore = require('../stores/channel_store.jsx'); var UserStore = require('../stores/user_store.jsx'); var UserProfile = require( './user_profile.jsx' ); var AsyncClient = require('../utils/async_client.jsx'); -var CreatePost = require('./create_post.jsx'); var Post = require('./post.jsx'); var LoadingScreen = require('./loading_screen.jsx'); var SocketStore = require('../stores/socket_store.jsx'); @@ -341,7 +340,7 @@ module.exports = React.createClass({ } } - if (channel.name === Constants.DEFAULT_CHANNEL) { + if (ChannelStore.isDefault(channel)) { more_messages = (

          Welcome

          diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx index 54858a04df..334fc6780c 100644 --- a/web/react/components/sidebar_header.jsx +++ b/web/react/components/sidebar_header.jsx @@ -37,17 +37,13 @@ var NavbarDropdown = React.createClass({ var invite_link = ""; var manage_link = ""; var rename_link = ""; - var currentUser = UserStore.getCurrentUser() + var currentUser = UserStore.getCurrentUser(); var isAdmin = false; if (currentUser != null) { isAdmin = currentUser.roles.indexOf("admin") > -1; - invite_link = ( -
        • - Invite New Member -
        • - ); + invite_link = (
        • Invite New Member
        • ); if (this.props.teamType == "O") { team_link = ( @@ -59,16 +55,8 @@ var NavbarDropdown = React.createClass({ } if (isAdmin) { - manage_link = ( -
        • - Manage Team -
        • - ); - rename_link = ( -
        • - Rename -
        • - ); + manage_link = (
        • Manage Team
        • ); + rename_link = (
        • Rename
        • ); } var teams = []; @@ -95,7 +83,7 @@ var NavbarDropdown = React.createClass({
          • Account Settings
          • - { isAdmin ?
          • Team Settings
          • : "" } + { isAdmin ?
          • Team Settings
          • : null } { invite_link } { team_link } { manage_link } @@ -113,28 +101,30 @@ var NavbarDropdown = React.createClass({ }); module.exports = React.createClass({ - handleSubmit: function(e) { - e.preventDefault(); - }, - getInitialState: function() { - return { }; + displayName: 'SidebarHeader', + + getDefaultProps: function() { + return { + teamName: config.SiteName + }; }, + render: function() { - var teamName = this.props.teamName ? this.props.teamName : config.SiteName; - var me = UserStore.getCurrentUser() + var me = UserStore.getCurrentUser(); + + if (!me) { + return null; + } return (
            -
            @{me.username}
            - { teamName } +
            {'@' + me.username}
            + {this.props.teamName}
          ); } }); - - - diff --git a/web/react/stores/channel_store.jsx b/web/react/stores/channel_store.jsx index 4a27e5f171..a97f133912 100644 --- a/web/react/stores/channel_store.jsx +++ b/web/react/stores/channel_store.jsx @@ -44,40 +44,24 @@ var ChannelStore = assign({}, EventEmitter.prototype, { removeExtraInfoChangeListener: function(callback) { this.removeListener(EXTRA_INFO_EVENT, callback); }, - get: function(id) { - var current = null; - var c = this._getChannels(); - - c.some(function(channel) { - if (channel.id == id) { - current = channel; - return true; + findFirstBy: function(field, value) { + var channels = this._getChannels(); + for (var i = 0; i < channels.length; i++) { + if (channels[i][field] == value) { + return channels[i]; } - return false; - }); + } - return current; + return null; + }, + get: function(id) { + return this.findFirstBy('id', id); }, getMember: function(id) { - var current = null; return this.getAllMembers()[id]; }, getByName: function(name) { - var current = null; - var c = this._getChannels(); - - c.some(function(channel) { - if (channel.name == name) { - current = channel; - return true; - } - - return false; - - }); - - return current; - + return this.findFirstBy('name', name); }, getAll: function() { return this._getChannels(); @@ -120,7 +104,7 @@ var ChannelStore = assign({}, EventEmitter.prototype, { getCurrent: function() { var currentId = this.getCurrentId(); - if (currentId != null) + if (currentId) return this.get(currentId); else return null; @@ -128,7 +112,7 @@ var ChannelStore = assign({}, EventEmitter.prototype, { getCurrentMember: function() { var currentId = ChannelStore.getCurrentId(); - if (currentId != null) + if (currentId) return this.getAllMembers()[currentId]; else return null; @@ -143,7 +127,7 @@ var ChannelStore = assign({}, EventEmitter.prototype, { var currentId = ChannelStore.getCurrentId(); var extra = null; - if (currentId != null) + if (currentId) extra = this._getExtraInfos()[currentId]; if (extra == null) @@ -154,7 +138,7 @@ var ChannelStore = assign({}, EventEmitter.prototype, { getExtraInfo: function(channel_id) { var extra = null; - if (channel_id != null) + if (channel_id) extra = this._getExtraInfos()[channel_id]; if (extra == null) @@ -192,7 +176,10 @@ var ChannelStore = assign({}, EventEmitter.prototype, { }, _getExtraInfos: function() { return BrowserStore.getItem("extra_infos", {}); - } + }, + isDefault: function(channel) { + return channel.name == Constants.DEFAULT_CHANNEL; + } }); ChannelStore.dispatchToken = AppDispatcher.register(function(payload) { @@ -231,4 +218,4 @@ ChannelStore.dispatchToken = AppDispatcher.register(function(payload) { } }); -module.exports = ChannelStore; +module.exports = ChannelStore; \ No newline at end of file diff --git a/web/react/stores/user_store.jsx b/web/react/stores/user_store.jsx index 93ddfec708..dd207ca80b 100644 --- a/web/react/stores/user_store.jsx +++ b/web/react/stores/user_store.jsx @@ -177,7 +177,7 @@ var UserStore = assign({}, EventEmitter.prototype, { }, getCurrentMentionKeys: function() { var user = this.getCurrentUser(); - if (user.notify_props && user.notify_props.mention_keys) { + if (user && user.notify_props && user.notify_props.mention_keys) { var keys = user.notify_props.mention_keys.split(','); if (user.full_name.length > 0 && user.notify_props.first_name === "true") { diff --git a/web/sass-files/sass/partials/_navbar.scss b/web/sass-files/sass/partials/_navbar.scss index 62864afb7b..6d8f11ce38 100644 --- a/web/sass-files/sass/partials/_navbar.scss +++ b/web/sass-files/sass/partials/_navbar.scss @@ -43,6 +43,7 @@ font-size: 16px; .heading { margin-right: 3px; + font-weight: 600; color: #fff; } .header-dropdown__icon { diff --git a/web/templates/head.html b/web/templates/head.html index ead6485713..5b423d487f 100644 --- a/web/templates/head.html +++ b/web/templates/head.html @@ -31,7 +31,6 @@ - @@ -102,5 +101,7 @@ } + + {{end}} From 6e67be40544f537d624f7f4449b8fcb97e338fb3 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Fri, 19 Jun 2015 16:04:33 -0700 Subject: [PATCH 34/90] Added framework for handling up and down arrow key presses to move between different @mention choices --- web/react/components/mention_list.jsx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index 103ff29bb2..3796d465f4 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -56,6 +56,22 @@ module.exports = React.createClass({ this.setState({ mentionText: '-1' }); }, + handleKeyDown: function(e) { + var selectedMention = this.state.selectedMention ? this.state.selectedMention : 1; + + // Need to be able to know number of mentions, use in conditionals & still + // need to figure out how to highlight the mention I want every time. + // Remember separate Mention Ref within for, second if statement in render + // Maybe have the call there instead? Ehhh maybe not but need that to be able + // to "select" it maybe... + if (e.key === "ArrowUp") { + selectedMention = selectedMention === ? 1 : selectedMention++; + } + else if (e.key === "ArrowDown") { + selectedMention = selectedMention === 1 ? : selectedMention--; + } + this.setState({selectedMention: selectedMention}); + } addFirstMention: function() { if (!this.refs.mention0) return; this.refs.mention0.handleClick(); @@ -144,7 +160,7 @@ module.exports = React.createClass({ return (
          -
          +
          { mentions }
          From f635376aa45e2fbaa30698949a0c991ea06aa320 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Fri, 19 Jun 2015 16:04:56 -0700 Subject: [PATCH 35/90] Added comma --- web/react/components/mention_list.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index 3796d465f4..a2c6f0ff78 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -71,7 +71,7 @@ module.exports = React.createClass({ selectedMention = selectedMention === 1 ? : selectedMention--; } this.setState({selectedMention: selectedMention}); - } + }, addFirstMention: function() { if (!this.refs.mention0) return; this.refs.mention0.handleClick(); From 924fd8609bb0b512cd60c1f345ec22dcb6bf0612 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Mon, 22 Jun 2015 17:39:55 -0700 Subject: [PATCH 36/90] Changed parts of framework --- web/react/components/mention_list.jsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index a2c6f0ff78..115ab1b2a5 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -56,8 +56,8 @@ module.exports = React.createClass({ this.setState({ mentionText: '-1' }); }, - handleKeyDown: function(e) { - var selectedMention = this.state.selectedMention ? this.state.selectedMention : 1; + handleKeyDown: function(e, numMentions) { + var selectedMention = this.state.selectedMention <= nunMentions ? this.state.selectedMention : 1; // Need to be able to know number of mentions, use in conditionals & still // need to figure out how to highlight the mention I want every time. @@ -65,10 +65,10 @@ module.exports = React.createClass({ // Maybe have the call there instead? Ehhh maybe not but need that to be able // to "select" it maybe... if (e.key === "ArrowUp") { - selectedMention = selectedMention === ? 1 : selectedMention++; + selectedMention = selectedMention === numMentions ? 1 : selectedMention++; } else if (e.key === "ArrowDown") { - selectedMention = selectedMention === 1 ? : selectedMention--; + selectedMention = selectedMention === 1 ? numMentions : selectedMention--; } this.setState({selectedMention: selectedMention}); }, @@ -89,7 +89,7 @@ module.exports = React.createClass({ return false; }, getInitialState: function() { - return { excludeUsers: [], mentionText: "-1" }; + return { excludeUsers: [], mentionText: "-1", selectedMention: 1 }; }, render: function() { var mentionText = this.state.mentionText; @@ -158,9 +158,11 @@ module.exports = React.createClass({ left: $mention_tab.offset().left }; + //mentions[this.state.selectedMention].focus(); + return (
          -
          +
          { mentions }
          From 63b9dd85b6efd9d292d4d79240e00b7d27e6616c Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Fri, 10 Jul 2015 09:49:44 -0700 Subject: [PATCH 37/90] Changed model of handling key down and focus change --- web/react/components/mention.jsx | 19 +++++++++++++- web/react/components/mention_list.jsx | 38 ++++++++++++++++----------- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/web/react/components/mention.jsx b/web/react/components/mention.jsx index 520b81cbb1..7a80451349 100644 --- a/web/react/components/mention.jsx +++ b/web/react/components/mention.jsx @@ -6,6 +6,23 @@ module.exports = React.createClass({ handleClick: function() { this.props.handleClick(this.props.username); }, + handleKeyDown: function(e) { + var selectedMention = this.state.selectedMention <= nunMentions ? this.state.selectedMention : 1; + + console.log("Here: keyDown"); + + if (e.key === "ArrowUp") { + //selectedMention = selectedMention === numMentions ? 1 : selectedMention++; + this.props.handleFocus(this.props.listId); + } + else if (e.key === "ArrowDown") { + //selectedMention = selectedMention === 1 ? numMentions : selectedMention--; + this.props.handleFocus(this.props.listId); + } + else if (e.key === "Enter") { + this.handleClick(); + } + }, render: function() { var icon; var timestamp = UserStore.getCurrentUser().update_at; @@ -15,7 +32,7 @@ module.exports = React.createClass({ icon = ; } return ( -
          +
          {icon}
          @{this.props.username}{this.props.secondary_text}
          diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index 115ab1b2a5..c5b4030c86 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -18,6 +18,8 @@ module.exports = React.createClass({ componentDidMount: function() { PostStore.addMentionDataChangeListener(this._onChange); + console.log("here!"); + var self = this; $('body').on('keypress.mentionlist', '#'+this.props.id, function(e) { @@ -26,6 +28,16 @@ module.exports = React.createClass({ e.preventDefault(); self.addFirstMention(); } + if (!self.isEmpty() && self.state.mentionText != '-1' && e.which === 38) { + e.stopPropagation(); + e.preventDefault(); + self.handleFocus(0); + } + if (!self.isEmpty() && self.state.mentionText != '-1' && e.which === 40) { + e.stopPropagation(); + e.preventDefault(); + self.addFirstMention(UserStore.getActiveOnlyProfiles().length - 1); + } } ); $(document).click(function(e) { @@ -56,21 +68,13 @@ module.exports = React.createClass({ this.setState({ mentionText: '-1' }); }, - handleKeyDown: function(e, numMentions) { - var selectedMention = this.state.selectedMention <= nunMentions ? this.state.selectedMention : 1; - - // Need to be able to know number of mentions, use in conditionals & still - // need to figure out how to highlight the mention I want every time. - // Remember separate Mention Ref within for, second if statement in render - // Maybe have the call there instead? Ehhh maybe not but need that to be able - // to "select" it maybe... - if (e.key === "ArrowUp") { - selectedMention = selectedMention === numMentions ? 1 : selectedMention++; - } - else if (e.key === "ArrowDown") { - selectedMention = selectedMention === 1 ? numMentions : selectedMention--; - } - this.setState({selectedMention: selectedMention}); + handleFocus: function(listId) { + console.log("here! 1"); + var mention = this.refs['mention' + index]; + if (!mention) return; + var mentionRef = mention.refs.mention; + mentionRef.getDOMNode().focus(); + console.log("here! 2"); }, addFirstMention: function() { if (!this.refs.mention0) return; @@ -140,6 +144,8 @@ module.exports = React.createClass({ username={users[i].username} secondary_text={users[i].secondary_text} id={users[i].id} + listId={index} + handleFocus={this.handleFocus} handleClick={this.handleClick} /> ); index++; @@ -162,7 +168,7 @@ module.exports = React.createClass({ return (
          -
          +
          { mentions }
          From 59b2a42ac89ca03a196bf5d20d617174a5bc30bb Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Sat, 11 Jul 2015 12:57:28 -0700 Subject: [PATCH 38/90] Have arrow key selection working, but visually does not highlight correct mention. Also if user continues to type things might get weird. --- web/react/components/mention.jsx | 23 ++++++++- web/react/components/mention_list.jsx | 56 ++++++++++++++------- web/sass-files/sass/partials/_mentions.scss | 4 ++ 3 files changed, 63 insertions(+), 20 deletions(-) diff --git a/web/react/components/mention.jsx b/web/react/components/mention.jsx index 7a80451349..abb6ae5c6a 100644 --- a/web/react/components/mention.jsx +++ b/web/react/components/mention.jsx @@ -6,22 +6,41 @@ module.exports = React.createClass({ handleClick: function() { this.props.handleClick(this.props.username); }, - handleKeyDown: function(e) { + /*handleUp: function(e) { var selectedMention = this.state.selectedMention <= nunMentions ? this.state.selectedMention : 1; console.log("Here: keyDown"); if (e.key === "ArrowUp") { //selectedMention = selectedMention === numMentions ? 1 : selectedMention++; + e.preventDefault(); this.props.handleFocus(this.props.listId); } else if (e.key === "ArrowDown") { //selectedMention = selectedMention === 1 ? numMentions : selectedMention--; + e.preventDefault(); this.props.handleFocus(this.props.listId); } else if (e.key === "Enter") { + e.preventDefault(); this.handleClick(); } + },*/ + handleFocus: function() { + console.log("Entering " + this.props.listId); + this.setState({ isFocused: "mentions-focus" }) + }, + handleBlur: function() { + console.log("Leaving " + this.props.listId); + this.setState({ isFocused: "" }); + }, + getInitialState: function() { + if (this.props.isFocus) { + return { isFocused: "mentions-focus" }; + } + else { + return { isFocused: "" }; + } }, render: function() { var icon; @@ -32,7 +51,7 @@ module.exports = React.createClass({ icon = ; } return ( -
          +
          {icon}
          @{this.props.username}{this.props.secondary_text}
          diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index c5b4030c86..882b0d6add 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -17,26 +17,44 @@ module.exports = React.createClass({ displayName: "MentionList", componentDidMount: function() { PostStore.addMentionDataChangeListener(this._onChange); - - console.log("here!"); - var self = this; $('body').on('keypress.mentionlist', '#'+this.props.id, function(e) { - if (!self.isEmpty() && self.state.mentionText != '-1' && e.which === 13) { + if (!self.isEmpty() && self.state.mentionText != '-1' && (e.which === 13 || e.which === 9)) { e.stopPropagation(); e.preventDefault(); - self.addFirstMention(); + self.addCurrentMention(); + } + } + ); + $('body').on('keydown.mentionlist', '#'+this.props.id, + function(e) { + console.log("here! top"); + if (!self.isEmpty() && self.state.mentionText != '-1' && (e.which === 13 || e.which === 9)) { + e.stopPropagation(); + e.preventDefault(); + self.addCurrentMention(); } if (!self.isEmpty() && self.state.mentionText != '-1' && e.which === 38) { + console.log("here! 38"); e.stopPropagation(); e.preventDefault(); - self.handleFocus(0); + + if (self.handleSelection(self.state.selectedMention - 1)) + self.setState({ selectedMention: self.state.selectedMention - 1 }); + else { + self.setState({ selectedMention: 0}); + } } if (!self.isEmpty() && self.state.mentionText != '-1' && e.which === 40) { + console.log("here! 40"); e.stopPropagation(); e.preventDefault(); - self.addFirstMention(UserStore.getActiveOnlyProfiles().length - 1); + + if (self.handleSelection(self.state.selectedMention + 1)) + self.setState({ selectedMention: self.state.selectedMention + 1 }); + else + self.setState({ selectedMention: 0 }); } } ); @@ -68,13 +86,17 @@ module.exports = React.createClass({ this.setState({ mentionText: '-1' }); }, - handleFocus: function(listId) { + handleSelection: function(listId) { console.log("here! 1"); - var mention = this.refs['mention' + index]; - if (!mention) return; - var mentionRef = mention.refs.mention; - mentionRef.getDOMNode().focus(); - console.log("here! 2"); + var mention = this.refs['mention' + listId]; + if (!mention) + return false; + else + return true; + }, + addCurrentMention: function() { + if (!this.refs['mention' + this.state.selectedMention]) return; + this.refs['mention' + this.state.selectedMention].handleClick(); }, addFirstMention: function() { if (!this.refs.mention0) return; @@ -93,7 +115,7 @@ module.exports = React.createClass({ return false; }, getInitialState: function() { - return { excludeUsers: [], mentionText: "-1", selectedMention: 1 }; + return { excludeUsers: [], mentionText: "-1", selectedMention: 0 }; }, render: function() { var mentionText = this.state.mentionText; @@ -138,14 +160,14 @@ module.exports = React.createClass({ if (firstName.lastIndexOf(mentionText,0) === 0 || lastName.lastIndexOf(mentionText,0) === 0 || users[i].username.lastIndexOf(mentionText,0) === 0) { - mentions[i+1] = ( + mentions[index] = ( ); index++; @@ -164,8 +186,6 @@ module.exports = React.createClass({ left: $mention_tab.offset().left }; - //mentions[this.state.selectedMention].focus(); - return (
          diff --git a/web/sass-files/sass/partials/_mentions.scss b/web/sass-files/sass/partials/_mentions.scss index 7e8c1869a6..1396f21a11 100644 --- a/web/sass-files/sass/partials/_mentions.scss +++ b/web/sass-files/sass/partials/_mentions.scss @@ -37,6 +37,10 @@ } } +.mentions-focus { + background-color: #E6F2FA; +} + .mentions-text { font-color:black; } From 9cc3d3428521fbc3a31def4f4780275d763a4011 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Sat, 11 Jul 2015 14:25:18 -0700 Subject: [PATCH 39/90] Highlighting now occurs as you arrow key thru mention list. However some small bugs remain. --- web/react/components/mention.jsx | 28 ++----------- web/react/components/mention_list.jsx | 57 +++++++++++++-------------- 2 files changed, 31 insertions(+), 54 deletions(-) diff --git a/web/react/components/mention.jsx b/web/react/components/mention.jsx index abb6ae5c6a..4abf8d0df1 100644 --- a/web/react/components/mention.jsx +++ b/web/react/components/mention.jsx @@ -6,32 +6,10 @@ module.exports = React.createClass({ handleClick: function() { this.props.handleClick(this.props.username); }, - /*handleUp: function(e) { - var selectedMention = this.state.selectedMention <= nunMentions ? this.state.selectedMention : 1; - - console.log("Here: keyDown"); - - if (e.key === "ArrowUp") { - //selectedMention = selectedMention === numMentions ? 1 : selectedMention++; - e.preventDefault(); - this.props.handleFocus(this.props.listId); - } - else if (e.key === "ArrowDown") { - //selectedMention = selectedMention === 1 ? numMentions : selectedMention--; - e.preventDefault(); - this.props.handleFocus(this.props.listId); - } - else if (e.key === "Enter") { - e.preventDefault(); - this.handleClick(); - } - },*/ - handleFocus: function() { - console.log("Entering " + this.props.listId); + select: function() { this.setState({ isFocused: "mentions-focus" }) }, - handleBlur: function() { - console.log("Leaving " + this.props.listId); + deselect: function() { this.setState({ isFocused: "" }); }, getInitialState: function() { @@ -51,7 +29,7 @@ module.exports = React.createClass({ icon = ; } return ( -
          +
          {icon}
          @{this.props.username}{this.props.secondary_text}
          diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index 882b0d6add..0bef41905d 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -18,43 +18,42 @@ module.exports = React.createClass({ componentDidMount: function() { PostStore.addMentionDataChangeListener(this._onChange); var self = this; - $('body').on('keypress.mentionlist', '#'+this.props.id, - function(e) { - if (!self.isEmpty() && self.state.mentionText != '-1' && (e.which === 13 || e.which === 9)) { - e.stopPropagation(); - e.preventDefault(); - self.addCurrentMention(); - } - } - ); + $('body').on('keydown.mentionlist', '#'+this.props.id, function(e) { - console.log("here! top"); if (!self.isEmpty() && self.state.mentionText != '-1' && (e.which === 13 || e.which === 9)) { e.stopPropagation(); e.preventDefault(); self.addCurrentMention(); } - if (!self.isEmpty() && self.state.mentionText != '-1' && e.which === 38) { - console.log("here! 38"); + else if (!self.isEmpty() && self.state.mentionText != '-1' && (e.which === 38 || e.which === 40)) { e.stopPropagation(); e.preventDefault(); - if (self.handleSelection(self.state.selectedMention - 1)) - self.setState({ selectedMention: self.state.selectedMention - 1 }); - else { - self.setState({ selectedMention: 0}); - } - } - if (!self.isEmpty() && self.state.mentionText != '-1' && e.which === 40) { - console.log("here! 40"); - e.stopPropagation(); - e.preventDefault(); - - if (self.handleSelection(self.state.selectedMention + 1)) - self.setState({ selectedMention: self.state.selectedMention + 1 }); - else + if (!self.getSelection(self.state.selectedMention)) { self.setState({ selectedMention: 0 }); + self.refs['mention' + self.state.selectedMention].deselect(); + } + else { + self.refs['mention' + self.state.selectedMention].deselect(); + if (e.which === 38) { + if (self.getSelection(self.state.selectedMention - 1)) + self.setState({ selectedMention: self.state.selectedMention - 1 }); + else { + var tempSelectedMention = -1; + while (self.getSelection(++tempSelectedMention)) + ; + self.setState({ selectedMention: tempSelectedMention - 1 }); + } + } + else { + if (self.getSelection(self.state.selectedMention + 1)) + self.setState({ selectedMention: self.state.selectedMention + 1 }); + else + self.setState({ selectedMention: 0 }); + } + } + self.refs['mention' + self.state.selectedMention].select(); } } ); @@ -86,8 +85,7 @@ module.exports = React.createClass({ this.setState({ mentionText: '-1' }); }, - handleSelection: function(listId) { - console.log("here! 1"); + getSelection: function(listId) { var mention = this.refs['mention' + listId]; if (!mention) return false; @@ -95,7 +93,7 @@ module.exports = React.createClass({ return true; }, addCurrentMention: function() { - if (!this.refs['mention' + this.state.selectedMention]) return; + if (!this.refs['mention' + this.state.selectedMention]) this.addFirstMention(); this.refs['mention' + this.state.selectedMention].handleClick(); }, addFirstMention: function() { @@ -173,6 +171,7 @@ module.exports = React.createClass({ index++; } } + var numMentions = Object.keys(mentions).length; if (numMentions < 1) return null; From ce9756e44dcdc1dedb46337f492f4ede8719fdd8 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Sun, 12 Jul 2015 12:34:51 -0700 Subject: [PATCH 40/90] Improved selection logic when using both the mouse and arrow keys --- web/react/components/mention.jsx | 3 ++- web/react/components/mention_list.jsx | 23 +++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/web/react/components/mention.jsx b/web/react/components/mention.jsx index 4abf8d0df1..1f6d1bcf1c 100644 --- a/web/react/components/mention.jsx +++ b/web/react/components/mention.jsx @@ -21,6 +21,7 @@ module.exports = React.createClass({ } }, render: function() { + var self = this; var icon; var timestamp = UserStore.getCurrentUser().update_at; if (this.props.id != null) { @@ -29,7 +30,7 @@ module.exports = React.createClass({ icon = ; } return ( -
          +
          {icon}
          @{this.props.username}{this.props.secondary_text}
          diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index 0bef41905d..6b1d98cf62 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -32,7 +32,7 @@ module.exports = React.createClass({ if (!self.getSelection(self.state.selectedMention)) { self.setState({ selectedMention: 0 }); - self.refs['mention' + self.state.selectedMention].deselect(); + //self.refs['mention' + self.state.selectedMention].deselect(); } else { self.refs['mention' + self.state.selectedMention].deselect(); @@ -54,6 +54,8 @@ module.exports = React.createClass({ } } self.refs['mention' + self.state.selectedMention].select(); + + //self.checkIfInView('#'+self.props.id); } } ); @@ -85,6 +87,13 @@ module.exports = React.createClass({ this.setState({ mentionText: '-1' }); }, + handleMouseEnter: function(listId) { + if (this.getSelection(this.state.selectedMention)) { + this.refs['mention' + this.state.selectedMention].deselect(); + } + this.setState({ selectedMention: listId }); + this.refs['mention' + listId].select(); + }, getSelection: function(listId) { var mention = this.refs['mention' + listId]; if (!mention) @@ -103,6 +112,15 @@ module.exports = React.createClass({ isEmpty: function() { return (!this.refs.mention0); }, + checkIfInView: function(element) { + var offset = element.offset().top - $(window).scrollTop(); + if(offset > window.innerHeight){ + // Not in view so scroll to it + $('body').animate({scrollTop: offset}, 1000); + return false; + } + return true; + }, alreadyMentioned: function(username) { var excludeUsers = this.state.excludeUsers; for (var i = 0; i < excludeUsers.length; i++) { @@ -113,7 +131,7 @@ module.exports = React.createClass({ return false; }, getInitialState: function() { - return { excludeUsers: [], mentionText: "-1", selectedMention: 0 }; + return { excludeUsers: [], mentionText: "-1", selectedMention: 0, selectedUsername: "" }; }, render: function() { var mentionText = this.state.mentionText; @@ -166,6 +184,7 @@ module.exports = React.createClass({ id={users[i].id} listId={index} isFocus={this.state.selectedMention === index ? true : false} + handleMouseEnter={this.handleMouseEnter} handleClick={this.handleClick} /> ); index++; From 6f93cbb490b41ec206b66018090f34e690a145a3 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Sun, 12 Jul 2015 12:49:33 -0700 Subject: [PATCH 41/90] When moving from a mention lower in the list to one higher in the list by typing more, now properly highlights --- web/react/components/mention_list.jsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index 6b1d98cf62..e6b2f5e04f 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -70,6 +70,11 @@ module.exports = React.createClass({ PostStore.removeMentionDataChangeListener(this._onChange); $('body').off('keypress.mentionlist', '#'+this.props.id); }, + componentDidUpdate: function() { + if (this.state.mentionText != "-1" && !this.getSelection(this.state.selectedMention)) { + this.refs.mention0.select(); + } + }, _onChange: function(id, mentionText, excludeList) { if (id !== this.props.id) return; From da228fc167494962dd222835510ff3a7db2e3a24 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Sun, 12 Jul 2015 18:39:14 -0700 Subject: [PATCH 42/90] Minor changes towards getting interior scrolling working with arrow keys --- web/react/components/mention_list.jsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index e6b2f5e04f..92a843bc59 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -55,7 +55,8 @@ module.exports = React.createClass({ } self.refs['mention' + self.state.selectedMention].select(); - //self.checkIfInView('#'+self.props.id); + self.checkIfInView($('#'+self.props.id)); + self.checkIfInView($('#'+self.refs['mention' + self.state.selectedMention].props.id)); } } ); @@ -118,10 +119,10 @@ module.exports = React.createClass({ return (!this.refs.mention0); }, checkIfInView: function(element) { - var offset = element.offset().top - $(window).scrollTop(); + var offset = element.offset().top - $(window).scrollTop(); //$(this.props.id) ?? if(offset > window.innerHeight){ // Not in view so scroll to it - $('body').animate({scrollTop: offset}, 1000); + $('body').animate({scrollTop: offset}, 1000); //$(this.props.id) ?? return false; } return true; @@ -136,7 +137,7 @@ module.exports = React.createClass({ return false; }, getInitialState: function() { - return { excludeUsers: [], mentionText: "-1", selectedMention: 0, selectedUsername: "" }; + return { excludeUsers: [], mentionText: "-1", selectedMention: 0 }; }, render: function() { var mentionText = this.state.mentionText; From 346f727949866083d8fd94763c6df5660424c850 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Mon, 13 Jul 2015 09:53:39 -0700 Subject: [PATCH 43/90] Better handling of previous highlight when you backspace (therefore re-enlarging the number of mentions) --- web/react/components/mention_list.jsx | 57 +++++++++++++++++++++------ 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index 92a843bc59..9dd80ebd5e 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -31,32 +31,40 @@ module.exports = React.createClass({ e.preventDefault(); if (!self.getSelection(self.state.selectedMention)) { - self.setState({ selectedMention: 0 }); + //self.setState({ selectedMention: 0, selectedUsername: self.refs.mention0.props.username }); //self.refs['mention' + self.state.selectedMention].deselect(); + var tempSelectedMention = -1 + while (self.getSelection(++tempSelectedMention)) { + if (self.state.selectedUsername === self.refs['mention' + tempSelectedMention].props.username) { + this.refs['mention' + tempSelectedMention].select(); + this.setState({ selectedMention: tempSelectedMention }); + break; + } + } } else { self.refs['mention' + self.state.selectedMention].deselect(); if (e.which === 38) { if (self.getSelection(self.state.selectedMention - 1)) - self.setState({ selectedMention: self.state.selectedMention - 1 }); + self.setState({ selectedMention: self.state.selectedMention - 1, selectedUsername: self.refs['mention' + (self.state.selectedMention - 1)].props.username }); else { var tempSelectedMention = -1; while (self.getSelection(++tempSelectedMention)) ; - self.setState({ selectedMention: tempSelectedMention - 1 }); + self.setState({ selectedMention: tempSelectedMention - 1, selectedUsername: self.refs['mention' + (tempSelectedMention - 1)].props.username }); } } else { if (self.getSelection(self.state.selectedMention + 1)) - self.setState({ selectedMention: self.state.selectedMention + 1 }); + self.setState({ selectedMention: self.state.selectedMention + 1, selectedUsername: self.refs['mention' + (self.state.selectedMention + 1)].props.username }); else - self.setState({ selectedMention: 0 }); + self.setState({ selectedMention: 0, selectedUsername: self.refs.mention0.props.username }); } } self.refs['mention' + self.state.selectedMention].select(); - self.checkIfInView($('#'+self.props.id)); - self.checkIfInView($('#'+self.refs['mention' + self.state.selectedMention].props.id)); + //self.checkIfInView($('#'+self.props.id)); + //self.checkIfInView($('#'+self.refs['mention' + self.state.selectedMention].props.id)); } } ); @@ -69,11 +77,36 @@ module.exports = React.createClass({ }, componentWillUnmount: function() { PostStore.removeMentionDataChangeListener(this._onChange); - $('body').off('keypress.mentionlist', '#'+this.props.id); + $('body').off('keydown.mentionlist', '#'+this.props.id); + }, + componentWillUpdate: function() { + if (this.state.mentionText != "-1" && this.getSelection(this.state.selectedMention)) { + this.refs['mention' + this.state.selectedMention].deselect(); + } }, componentDidUpdate: function() { - if (this.state.mentionText != "-1" && !this.getSelection(this.state.selectedMention)) { - this.refs.mention0.select(); + /*if (this.state.mentionText != "-1" && !this.getSelection(this.state.selectedMention)) { + }*/ + if (this.state.mentionText != "-1" /*&& this.getSelection(this.state.selectedMention)*/) { + if (this.state.selectedUsername !== "" && (!this.getSelection(this.state.selectedMention) || this.state.selectedUsername !== this.refs['mention' + this.state.selectedMention].props.username)) { + var tempSelectedMention = -1; + var foundMatch = false; + while (this.getSelection(++tempSelectedMention)) { + if (this.state.selectedUsername === this.refs['mention' + tempSelectedMention].props.username) { + this.refs['mention' + tempSelectedMention].select(); + this.setState({ selectedMention: tempSelectedMention }); + foundMatch = true; + break; + } + } + if (!foundMatch) { + this.refs.mention0.select(); + this.setState({ selectedMention: 0, selectedUsername: this.refs.mention0.props.username }); + } + } + else { + this.refs['mention' + this.state.selectedMention].select(); + } } }, _onChange: function(id, mentionText, excludeList) { @@ -97,7 +130,7 @@ module.exports = React.createClass({ if (this.getSelection(this.state.selectedMention)) { this.refs['mention' + this.state.selectedMention].deselect(); } - this.setState({ selectedMention: listId }); + this.setState({ selectedMention: listId, selectedUsername: this.refs['mention' + listId].props.username }); this.refs['mention' + listId].select(); }, getSelection: function(listId) { @@ -137,7 +170,7 @@ module.exports = React.createClass({ return false; }, getInitialState: function() { - return { excludeUsers: [], mentionText: "-1", selectedMention: 0 }; + return { excludeUsers: [], mentionText: "-1", selectedMention: 0, selectedUsername: "" }; }, render: function() { var mentionText = this.state.mentionText; From 4bb2451bd5ac7f570e510a0cb96c14f0b17e4fab Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Mon, 13 Jul 2015 13:11:03 -0700 Subject: [PATCH 44/90] Minor changes and tweaks trying to get scrolling to work --- web/react/components/mention_list.jsx | 31 +++++++++++++++++---------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index 9dd80ebd5e..ab511e4249 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -50,7 +50,7 @@ module.exports = React.createClass({ else { var tempSelectedMention = -1; while (self.getSelection(++tempSelectedMention)) - ; + ; //Need to find the top of the list self.setState({ selectedMention: tempSelectedMention - 1, selectedUsername: self.refs['mention' + (tempSelectedMention - 1)].props.username }); } } @@ -64,7 +64,10 @@ module.exports = React.createClass({ self.refs['mention' + self.state.selectedMention].select(); //self.checkIfInView($('#'+self.props.id)); - //self.checkIfInView($('#'+self.refs['mention' + self.state.selectedMention].props.id)); + self.checkIfInView(); + //console.log('#'+self.refs['mention' + self.state.selectedMention].props.id); + //console.log($('#'+self.refs['mention' + self.state.selectedMention].props.id)); + //console.log($('#'+self.props.id)); } } ); @@ -85,9 +88,7 @@ module.exports = React.createClass({ } }, componentDidUpdate: function() { - /*if (this.state.mentionText != "-1" && !this.getSelection(this.state.selectedMention)) { - }*/ - if (this.state.mentionText != "-1" /*&& this.getSelection(this.state.selectedMention)*/) { + if (this.state.mentionText != "-1") { if (this.state.selectedUsername !== "" && (!this.getSelection(this.state.selectedMention) || this.state.selectedUsername !== this.refs['mention' + this.state.selectedMention].props.username)) { var tempSelectedMention = -1; var foundMatch = false; @@ -141,8 +142,10 @@ module.exports = React.createClass({ return true; }, addCurrentMention: function() { - if (!this.refs['mention' + this.state.selectedMention]) this.addFirstMention(); - this.refs['mention' + this.state.selectedMention].handleClick(); + if (!this.refs['mention' + this.state.selectedMention]) + this.addFirstMention(); + else + this.refs['mention' + this.state.selectedMention].handleClick(); }, addFirstMention: function() { if (!this.refs.mention0) return; @@ -151,14 +154,20 @@ module.exports = React.createClass({ isEmpty: function() { return (!this.refs.mention0); }, - checkIfInView: function(element) { - var offset = element.offset().top - $(window).scrollTop(); //$(this.props.id) ?? - if(offset > window.innerHeight){ + checkIfInView: function() { + var element = $('#'+this.refs['mention' + this.state.selectedMention].props.id); + //var top = $('.mentions-box').position().bottom; + //$(".mentions-box").css("bottom", top+303); + //$("#"+this.props.id).scrollTop($("#"+this.props.id).scrollTop() + $("div.mentions-name.mentions-focused").position().top); + console.log(element.length); + console.log(element + " " + element.offset()); + var offset = element.offset().bottom - $('#'+this.props.id).scrollTop(); //$(this.props.id) ?? + if(offset > $('#'+this.props.id).innerHeight){ // Not in view so scroll to it $('body').animate({scrollTop: offset}, 1000); //$(this.props.id) ?? return false; } - return true; + return true; }, alreadyMentioned: function(username) { var excludeUsers = this.state.excludeUsers; From 61e7b9b2be969212dbb3577ef734a27620a525be Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Tue, 14 Jul 2015 16:13:31 -0700 Subject: [PATCH 45/90] Getting scrolling working with the arrow keys --- web/react/components/mention.jsx | 2 +- web/react/components/mention_list.jsx | 57 ++++++++++++++++++++------- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/web/react/components/mention.jsx b/web/react/components/mention.jsx index 1f6d1bcf1c..3f6ec4a7b6 100644 --- a/web/react/components/mention.jsx +++ b/web/react/components/mention.jsx @@ -30,7 +30,7 @@ module.exports = React.createClass({ icon = ; } return ( -
          +
          {icon}
          @{this.props.username}{this.props.secondary_text}
          diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index ab511e4249..2f4f92e24e 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -30,6 +30,8 @@ module.exports = React.createClass({ e.stopPropagation(); e.preventDefault(); + var tempSelectedMention = -1 + if (!self.getSelection(self.state.selectedMention)) { //self.setState({ selectedMention: 0, selectedUsername: self.refs.mention0.props.username }); //self.refs['mention' + self.state.selectedMention].deselect(); @@ -64,7 +66,7 @@ module.exports = React.createClass({ self.refs['mention' + self.state.selectedMention].select(); //self.checkIfInView($('#'+self.props.id)); - self.checkIfInView(); + self.checkIfInView(e.which, tempSelectedMention); //console.log('#'+self.refs['mention' + self.state.selectedMention].props.id); //console.log($('#'+self.refs['mention' + self.state.selectedMention].props.id)); //console.log($('#'+self.props.id)); @@ -154,20 +156,43 @@ module.exports = React.createClass({ isEmpty: function() { return (!this.refs.mention0); }, - checkIfInView: function() { - var element = $('#'+this.refs['mention' + this.state.selectedMention].props.id); - //var top = $('.mentions-box').position().bottom; - //$(".mentions-box").css("bottom", top+303); - //$("#"+this.props.id).scrollTop($("#"+this.props.id).scrollTop() + $("div.mentions-name.mentions-focused").position().top); - console.log(element.length); - console.log(element + " " + element.offset()); - var offset = element.offset().bottom - $('#'+this.props.id).scrollTop(); //$(this.props.id) ?? - if(offset > $('#'+this.props.id).innerHeight){ - // Not in view so scroll to it - $('body').animate({scrollTop: offset}, 1000); //$(this.props.id) ?? - return false; + checkIfInView: function(keyPressed, ifLoopUp) { + var element = $('#'+this.refs['mention' + this.state.selectedMention].props.id +"_mentions"); + var offset = element.offset().top - $("#mentionsbox").scrollTop(); + var direction = keyPressed === 38 ? "up" : "down"; + console.log("element offset top: " + $(element).offset().top + " box offset top: " + $("#mentionsbox").offset().top); + var scrollAmount; + if (direction === "up" && ifLoopUp !== -1) { + /*$("#mentionsbox").animate({ + scrollTop: ("#mentionsbox").offset().top + }, 50);*/ + scrollAmount = $("#mentionsbox").offset().top; } - return true; + else if (direction === "down" && !this.refs['mention' + this.state.selectedMention].props.listId) { + /*$("#mentionsbox").animate({ + scrollTop: 0 + }, 50);*/ + scrollAmount = 0; + } + else if (direction === "up") { + /*$("#mentionsbox").animate({ + scrollTop: '-=28' + }, 50);*/ + scrollAmount = "-=28"; + } + else if (direction === "down") { + /*$("#mentionsbox").animate({ + scrollTop: '+=28' + }, 50);*/ + scrollAmount = "+=28"; + } + $("#mentionsbox").animate({ + scrollTop: scrollAmount + }, 50); + /*$("#mentionsbox").animate({ + scrollTop: $(element).offset().top - $("#mentionsbox").offset().top + //scrollTop: $(element).offset().top - $("#mentionsbox").offset().top + }, 50);*/ }, alreadyMentioned: function(username) { var excludeUsers = this.state.excludeUsers; @@ -195,12 +220,14 @@ module.exports = React.createClass({ all.username = "all"; all.full_name = ""; all.secondary_text = "Notifies everyone in the team"; + all.id = "allmention"; users.push(all); var channel = {}; channel.username = "channel"; channel.full_name = ""; channel.secondary_text = "Notifies everyone in the channel"; + channel.id = "channelmention"; users.push(channel); users.sort(function(a,b) { @@ -254,7 +281,7 @@ module.exports = React.createClass({ return (
          -
          +
          { mentions }
          From 9747053a4e017e64a21278981fe43573385488a0 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Tue, 14 Jul 2015 18:36:54 -0700 Subject: [PATCH 46/90] Fixes not selecting an element that hasn't been scrolled to --- web/react/components/mention.jsx | 4 +- web/react/components/mention_list.jsx | 70 +++++++++------------------ 2 files changed, 27 insertions(+), 47 deletions(-) diff --git a/web/react/components/mention.jsx b/web/react/components/mention.jsx index 3f6ec4a7b6..24098516e9 100644 --- a/web/react/components/mention.jsx +++ b/web/react/components/mention.jsx @@ -24,7 +24,9 @@ module.exports = React.createClass({ var self = this; var icon; var timestamp = UserStore.getCurrentUser().update_at; - if (this.props.id != null) { + if (this.props.id === "allmention" || this.props.id === "channelmention") { + icon = ; + } else if (this.props.id != null) { icon = ; diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index 2f4f92e24e..7cc90e36fd 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -33,9 +33,6 @@ module.exports = React.createClass({ var tempSelectedMention = -1 if (!self.getSelection(self.state.selectedMention)) { - //self.setState({ selectedMention: 0, selectedUsername: self.refs.mention0.props.username }); - //self.refs['mention' + self.state.selectedMention].deselect(); - var tempSelectedMention = -1 while (self.getSelection(++tempSelectedMention)) { if (self.state.selectedUsername === self.refs['mention' + tempSelectedMention].props.username) { this.refs['mention' + tempSelectedMention].select(); @@ -50,7 +47,6 @@ module.exports = React.createClass({ if (self.getSelection(self.state.selectedMention - 1)) self.setState({ selectedMention: self.state.selectedMention - 1, selectedUsername: self.refs['mention' + (self.state.selectedMention - 1)].props.username }); else { - var tempSelectedMention = -1; while (self.getSelection(++tempSelectedMention)) ; //Need to find the top of the list self.setState({ selectedMention: tempSelectedMention - 1, selectedUsername: self.refs['mention' + (tempSelectedMention - 1)].props.username }); @@ -65,11 +61,10 @@ module.exports = React.createClass({ } self.refs['mention' + self.state.selectedMention].select(); - //self.checkIfInView($('#'+self.props.id)); - self.checkIfInView(e.which, tempSelectedMention); - //console.log('#'+self.refs['mention' + self.state.selectedMention].props.id); - //console.log($('#'+self.refs['mention' + self.state.selectedMention].props.id)); - //console.log($('#'+self.props.id)); + self.scrollToMention(e.which, tempSelectedMention); + } + else if (e.which === 46 || e.which === 8) { + self.setState({ lessText: true }); } } ); @@ -94,7 +89,7 @@ module.exports = React.createClass({ if (this.state.selectedUsername !== "" && (!this.getSelection(this.state.selectedMention) || this.state.selectedUsername !== this.refs['mention' + this.state.selectedMention].props.username)) { var tempSelectedMention = -1; var foundMatch = false; - while (this.getSelection(++tempSelectedMention)) { + while (!this.state.lessText && this.getSelection(++tempSelectedMention)) { if (this.state.selectedUsername === this.refs['mention' + tempSelectedMention].props.username) { this.refs['mention' + tempSelectedMention].select(); this.setState({ selectedMention: tempSelectedMention }); @@ -102,15 +97,18 @@ module.exports = React.createClass({ break; } } - if (!foundMatch) { + if (this.refs.mention0 != undefined && !foundMatch) { this.refs.mention0.select(); - this.setState({ selectedMention: 0, selectedUsername: this.refs.mention0.props.username }); + this.setState({ selectedMention: 0, selectedUsername: this.refs.mention0.props.username, lessText: false }); } } - else { + else if (this.refs['mention' + this.state.selectedMention] != undefined) { this.refs['mention' + this.state.selectedMention].select(); } } + else if (this.state.selectedMention !== 0) { + this.setState({ selectedMention: 0, selectedUsername: "" }); + } }, _onChange: function(id, mentionText, excludeList) { if (id !== this.props.id) return; @@ -118,6 +116,7 @@ module.exports = React.createClass({ var newState = this.state; if (mentionText != null) newState.mentionText = mentionText; if (excludeList != null) newState.excludeUsers = excludeList; + this.setState(newState); }, handleClick: function(name) { @@ -156,43 +155,22 @@ module.exports = React.createClass({ isEmpty: function() { return (!this.refs.mention0); }, - checkIfInView: function(keyPressed, ifLoopUp) { - var element = $('#'+this.refs['mention' + this.state.selectedMention].props.id +"_mentions"); - var offset = element.offset().top - $("#mentionsbox").scrollTop(); + scrollToMention: function(keyPressed, ifLoopUp) { var direction = keyPressed === 38 ? "up" : "down"; - console.log("element offset top: " + $(element).offset().top + " box offset top: " + $("#mentionsbox").offset().top); - var scrollAmount; - if (direction === "up" && ifLoopUp !== -1) { - /*$("#mentionsbox").animate({ - scrollTop: ("#mentionsbox").offset().top - }, 50);*/ - scrollAmount = $("#mentionsbox").offset().top; - } - else if (direction === "down" && !this.refs['mention' + this.state.selectedMention].props.listId) { - /*$("#mentionsbox").animate({ - scrollTop: 0 - }, 50);*/ + var scrollAmount = 0; + + if (direction === "up" && ifLoopUp !== -1) + scrollAmount = $("#mentionsbox").innerHeight() + 10000000; //innerHeight is not the real height of the box in the RHS sometimes; this compensates as it should always go to the bottom anyway + else if (direction === "down" && this.refs['mention' + this.state.selectedMention].props.listId === 0) scrollAmount = 0; - } - else if (direction === "up") { - /*$("#mentionsbox").animate({ - scrollTop: '-=28' - }, 50);*/ - scrollAmount = "-=28"; - } - else if (direction === "down") { - /*$("#mentionsbox").animate({ - scrollTop: '+=28' - }, 50);*/ - scrollAmount = "+=28"; - } + else if (direction === "up") + scrollAmount = "-=" + ($('#'+this.refs['mention' + this.state.selectedMention].props.id +"_mentions").innerHeight() - 5); + else if (direction === "down") + scrollAmount = "+=" + ($('#'+this.refs['mention' + this.state.selectedMention].props.id +"_mentions").innerHeight() - 5); + $("#mentionsbox").animate({ scrollTop: scrollAmount }, 50); - /*$("#mentionsbox").animate({ - scrollTop: $(element).offset().top - $("#mentionsbox").offset().top - //scrollTop: $(element).offset().top - $("#mentionsbox").offset().top - }, 50);*/ }, alreadyMentioned: function(username) { var excludeUsers = this.state.excludeUsers; @@ -204,7 +182,7 @@ module.exports = React.createClass({ return false; }, getInitialState: function() { - return { excludeUsers: [], mentionText: "-1", selectedMention: 0, selectedUsername: "" }; + return { excludeUsers: [], mentionText: "-1", selectedMention: 0, selectedUsername: "", lessText: false }; }, render: function() { var mentionText = this.state.mentionText; From c5c4c0b4de6275ca3f4188c07d2d0d88ba230bd1 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Tue, 14 Jul 2015 19:59:09 -0700 Subject: [PATCH 47/90] Few minor bug fixes --- web/react/components/mention_list.jsx | 50 +++++++++------------------ 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index 7cc90e36fd..17c5137146 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -31,41 +31,26 @@ module.exports = React.createClass({ e.preventDefault(); var tempSelectedMention = -1 - - if (!self.getSelection(self.state.selectedMention)) { - while (self.getSelection(++tempSelectedMention)) { - if (self.state.selectedUsername === self.refs['mention' + tempSelectedMention].props.username) { - this.refs['mention' + tempSelectedMention].select(); - this.setState({ selectedMention: tempSelectedMention }); - break; - } + self.refs['mention' + self.state.selectedMention].deselect(); + if (e.which === 38) { + if (self.getSelection(self.state.selectedMention - 1)) + self.setState({ selectedMention: self.state.selectedMention - 1, selectedUsername: self.refs['mention' + (self.state.selectedMention - 1)].props.username }); + else { + while (self.getSelection(++tempSelectedMention)) + ; //Need to find the top of the list + self.setState({ selectedMention: tempSelectedMention - 1, selectedUsername: self.refs['mention' + (tempSelectedMention - 1)].props.username }); } } else { - self.refs['mention' + self.state.selectedMention].deselect(); - if (e.which === 38) { - if (self.getSelection(self.state.selectedMention - 1)) - self.setState({ selectedMention: self.state.selectedMention - 1, selectedUsername: self.refs['mention' + (self.state.selectedMention - 1)].props.username }); - else { - while (self.getSelection(++tempSelectedMention)) - ; //Need to find the top of the list - self.setState({ selectedMention: tempSelectedMention - 1, selectedUsername: self.refs['mention' + (tempSelectedMention - 1)].props.username }); - } - } - else { - if (self.getSelection(self.state.selectedMention + 1)) - self.setState({ selectedMention: self.state.selectedMention + 1, selectedUsername: self.refs['mention' + (self.state.selectedMention + 1)].props.username }); - else - self.setState({ selectedMention: 0, selectedUsername: self.refs.mention0.props.username }); - } + if (self.getSelection(self.state.selectedMention + 1)) + self.setState({ selectedMention: self.state.selectedMention + 1, selectedUsername: self.refs['mention' + (self.state.selectedMention + 1)].props.username }); + else + self.setState({ selectedMention: 0, selectedUsername: self.refs.mention0.props.username }); } - self.refs['mention' + self.state.selectedMention].select(); + self.refs['mention' + self.state.selectedMention].select(); self.scrollToMention(e.which, tempSelectedMention); } - else if (e.which === 46 || e.which === 8) { - self.setState({ lessText: true }); - } } ); $(document).click(function(e) { @@ -89,7 +74,7 @@ module.exports = React.createClass({ if (this.state.selectedUsername !== "" && (!this.getSelection(this.state.selectedMention) || this.state.selectedUsername !== this.refs['mention' + this.state.selectedMention].props.username)) { var tempSelectedMention = -1; var foundMatch = false; - while (!this.state.lessText && this.getSelection(++tempSelectedMention)) { + while (tempSelectedMention < this.state.selectedMention && this.getSelection(++tempSelectedMention)) { if (this.state.selectedUsername === this.refs['mention' + tempSelectedMention].props.username) { this.refs['mention' + tempSelectedMention].select(); this.setState({ selectedMention: tempSelectedMention }); @@ -99,7 +84,7 @@ module.exports = React.createClass({ } if (this.refs.mention0 != undefined && !foundMatch) { this.refs.mention0.select(); - this.setState({ selectedMention: 0, selectedUsername: this.refs.mention0.props.username, lessText: false }); + this.setState({ selectedMention: 0, selectedUsername: this.refs.mention0.props.username }); } } else if (this.refs['mention' + this.state.selectedMention] != undefined) { @@ -136,8 +121,7 @@ module.exports = React.createClass({ this.refs['mention' + listId].select(); }, getSelection: function(listId) { - var mention = this.refs['mention' + listId]; - if (!mention) + if (!this.refs['mention' + listId]) return false; else return true; @@ -182,7 +166,7 @@ module.exports = React.createClass({ return false; }, getInitialState: function() { - return { excludeUsers: [], mentionText: "-1", selectedMention: 0, selectedUsername: "", lessText: false }; + return { excludeUsers: [], mentionText: "-1", selectedMention: 0, selectedUsername: "" }; }, render: function() { var mentionText = this.state.mentionText; From cbcf44ac8506d316d57a7e0bb30baad258618bdb Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Tue, 14 Jul 2015 20:14:23 -0700 Subject: [PATCH 48/90] Minor changes to scroll 'feel' and logic flow --- web/react/components/mention_list.jsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index 17c5137146..1852a3ebc5 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -41,7 +41,7 @@ module.exports = React.createClass({ self.setState({ selectedMention: tempSelectedMention - 1, selectedUsername: self.refs['mention' + (tempSelectedMention - 1)].props.username }); } } - else { + else if (e.which === 40) { if (self.getSelection(self.state.selectedMention + 1)) self.setState({ selectedMention: self.state.selectedMention + 1, selectedUsername: self.refs['mention' + (self.state.selectedMention + 1)].props.username }); else @@ -82,12 +82,12 @@ module.exports = React.createClass({ break; } } - if (this.refs.mention0 != undefined && !foundMatch) { + if (this.getSelection(0) && !foundMatch) { this.refs.mention0.select(); this.setState({ selectedMention: 0, selectedUsername: this.refs.mention0.props.username }); } } - else if (this.refs['mention' + this.state.selectedMention] != undefined) { + else if (this.getSelection(this.state.selectedMention)) { this.refs['mention' + this.state.selectedMention].select(); } } @@ -144,7 +144,7 @@ module.exports = React.createClass({ var scrollAmount = 0; if (direction === "up" && ifLoopUp !== -1) - scrollAmount = $("#mentionsbox").innerHeight() + 10000000; //innerHeight is not the real height of the box in the RHS sometimes; this compensates as it should always go to the bottom anyway + scrollAmount = $("#mentionsbox").height() * 100; //Makes sure that it scrolls all the way to the bottom else if (direction === "down" && this.refs['mention' + this.state.selectedMention].props.listId === 0) scrollAmount = 0; else if (direction === "up") @@ -154,7 +154,7 @@ module.exports = React.createClass({ $("#mentionsbox").animate({ scrollTop: scrollAmount - }, 50); + }, 75); }, alreadyMentioned: function(username) { var excludeUsers = this.state.excludeUsers; From 15b90ff72f7db4d611c5c5035e2d8cd310d4deff Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Tue, 14 Jul 2015 20:35:52 -0700 Subject: [PATCH 49/90] Added missing semi-colons --- web/react/components/mention.jsx | 2 +- web/react/components/mention_list.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/react/components/mention.jsx b/web/react/components/mention.jsx index 24098516e9..f5ca4ce6d9 100644 --- a/web/react/components/mention.jsx +++ b/web/react/components/mention.jsx @@ -7,7 +7,7 @@ module.exports = React.createClass({ this.props.handleClick(this.props.username); }, select: function() { - this.setState({ isFocused: "mentions-focus" }) + this.setState({ isFocused: "mentions-focus" }); }, deselect: function() { this.setState({ isFocused: "" }); diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index 1852a3ebc5..1df7a8298a 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -30,7 +30,7 @@ module.exports = React.createClass({ e.stopPropagation(); e.preventDefault(); - var tempSelectedMention = -1 + var tempSelectedMention = -1; self.refs['mention' + self.state.selectedMention].deselect(); if (e.which === 38) { if (self.getSelection(self.state.selectedMention - 1)) From 463e89c280d50c017d8ca5baef5edf90ff9299a7 Mon Sep 17 00:00:00 2001 From: it33 Date: Thu, 16 Jul 2015 08:17:19 -0700 Subject: [PATCH 50/90] Clarifying license in README.md --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 04cbf718de..a4f95dbdf8 100644 --- a/README.md +++ b/README.md @@ -137,8 +137,5 @@ To contribute to this open source project please review the Mattermost Contribut License ------- -Most Mattermost source files are made available under the terms of the GNU Affero General Public License (AGPL). See individual files for details. - -As an exception, Admin Tools and Configuration Files are are made available under the terms of the Apache License, version 2.0. See LICENSE.txt for more information. - +Mattermost is licensed under an "Apache-wrapped AGPL" model, which means you can run and link to the system using Configuration Files and Admin Tools licensed under Apache, version 2.0, as described in the LICENSE file, as an explicit exception to the terms of the GNU Affero General Public License (AGPL) that applies to most of the remaining source files. See individual files for details. From 139f6611d6162158ea88c679aee710f7a6c76c49 Mon Sep 17 00:00:00 2001 From: nickago Date: Thu, 16 Jul 2015 08:55:37 -0700 Subject: [PATCH 51/90] Added last updated for pictures --- api/user.go | 2 +- model/user.go | 1 + store/sql_user_store.go | 7 +++++-- store/store.go | 2 +- web/react/components/user_settings.jsx | 9 +++++++-- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/api/user.go b/api/user.go index 0c63868b32..3c0062f8c1 100644 --- a/api/user.go +++ b/api/user.go @@ -729,7 +729,7 @@ func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) { return } - Srv.Store.User().UpdateUpdateAt(c.Session.UserId) + Srv.Store.User().UpdateLastPictureUpdate(c.Session.UserId) c.LogAudit("") } diff --git a/model/user.go b/model/user.go index b94ceb899a..c5a4d846dc 100644 --- a/model/user.go +++ b/model/user.go @@ -45,6 +45,7 @@ type User struct { Props StringMap `json:"props"` NotifyProps StringMap `json:"notify_props"` LastPasswordUpdate int64 `json:"last_password_update"` + LastPictureUpdate int64 `json:"last_picture_update"` } // IsValid validates the user and returns an error if it isn't configured diff --git a/store/sql_user_store.go b/store/sql_user_store.go index 77470946c6..dd11dd0ae4 100644 --- a/store/sql_user_store.go +++ b/store/sql_user_store.go @@ -120,6 +120,7 @@ func (us SqlUserStore) Update(user *model.User, allowActiveUpdate bool) StoreCha user.AuthData = oldUser.AuthData user.Password = oldUser.Password user.LastPasswordUpdate = oldUser.LastPasswordUpdate + user.LastPictureUpdate = oldUser.LastPictureUpdate user.TeamId = oldUser.TeamId user.LastActivityAt = oldUser.LastActivityAt user.LastPingAt = oldUser.LastPingAt @@ -150,13 +151,15 @@ func (us SqlUserStore) Update(user *model.User, allowActiveUpdate bool) StoreCha return storeChannel } -func (us SqlUserStore) UpdateUpdateAt(userId string) StoreChannel { +func (us SqlUserStore) UpdateLastPictureUpdate(userId string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} - if _, err := us.GetMaster().Exec("UPDATE Users SET UpdateAt = ? WHERE Id = ?", model.GetMillis(), userId); err != nil { + curTime := model.GetMillis() + + if _, err := us.GetMaster().Exec("UPDATE Users SET LastPictureUpdate = ?, UpdateAt = ? WHERE Id = ?", curTime, curTime, userId); err != nil { result.Err = model.NewAppError("SqlUserStore.UpdateUpdateAt", "We couldn't update the update_at", "user_id="+userId) } else { result.Data = userId diff --git a/store/store.go b/store/store.go index 0ed0457887..9faa6a9d74 100644 --- a/store/store.go +++ b/store/store.go @@ -77,7 +77,7 @@ type PostStore interface { type UserStore interface { Save(user *model.User) StoreChannel Update(user *model.User, allowRoleUpdate bool) StoreChannel - UpdateUpdateAt(userId string) StoreChannel + UpdateLastPictureUpdate(userId string) StoreChannel UpdateLastPingAt(userId string, time int64) StoreChannel UpdateLastActivityAt(userId string, time int64) StoreChannel UpdateUserAndSessionActivity(userId string, sessionId string, time int64) StoreChannel diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx index 06d8d0208b..b1ccd125a3 100644 --- a/web/react/components/user_settings.jsx +++ b/web/react/components/user_settings.jsx @@ -820,6 +820,7 @@ var GeneralTab = React.createClass({ client.uploadProfileImage(formData, function(data) { this.submitActive = false; + AsyncClient.getMe(); window.location.reload(); }.bind(this), function(err) { @@ -989,7 +990,7 @@ var GeneralTab = React.createClass({ ); From cb4cd741a45fc86a7c2d96e61abd65ad4bd728e1 Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Thu, 16 Jul 2015 09:21:51 -0700 Subject: [PATCH 52/90] Code simplification and rebasing --- web/react/components/mention.jsx | 15 ++------------- web/react/components/mention_list.jsx | 23 ++++------------------- 2 files changed, 6 insertions(+), 32 deletions(-) diff --git a/web/react/components/mention.jsx b/web/react/components/mention.jsx index f5ca4ce6d9..114dc183f7 100644 --- a/web/react/components/mention.jsx +++ b/web/react/components/mention.jsx @@ -6,19 +6,8 @@ module.exports = React.createClass({ handleClick: function() { this.props.handleClick(this.props.username); }, - select: function() { - this.setState({ isFocused: "mentions-focus" }); - }, - deselect: function() { - this.setState({ isFocused: "" }); - }, getInitialState: function() { - if (this.props.isFocus) { - return { isFocused: "mentions-focus" }; - } - else { - return { isFocused: "" }; - } + return null; }, render: function() { var self = this; @@ -32,7 +21,7 @@ module.exports = React.createClass({ icon = ; } return ( -
          +
          {icon}
          @{this.props.username}{this.props.secondary_text}
          diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index 1df7a8298a..fff0c10b8f 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -31,7 +31,6 @@ module.exports = React.createClass({ e.preventDefault(); var tempSelectedMention = -1; - self.refs['mention' + self.state.selectedMention].deselect(); if (e.which === 38) { if (self.getSelection(self.state.selectedMention - 1)) self.setState({ selectedMention: self.state.selectedMention - 1, selectedUsername: self.refs['mention' + (self.state.selectedMention - 1)].props.username }); @@ -48,7 +47,6 @@ module.exports = React.createClass({ self.setState({ selectedMention: 0, selectedUsername: self.refs.mention0.props.username }); } - self.refs['mention' + self.state.selectedMention].select(); self.scrollToMention(e.which, tempSelectedMention); } } @@ -64,11 +62,6 @@ module.exports = React.createClass({ PostStore.removeMentionDataChangeListener(this._onChange); $('body').off('keydown.mentionlist', '#'+this.props.id); }, - componentWillUpdate: function() { - if (this.state.mentionText != "-1" && this.getSelection(this.state.selectedMention)) { - this.refs['mention' + this.state.selectedMention].deselect(); - } - }, componentDidUpdate: function() { if (this.state.mentionText != "-1") { if (this.state.selectedUsername !== "" && (!this.getSelection(this.state.selectedMention) || this.state.selectedUsername !== this.refs['mention' + this.state.selectedMention].props.username)) { @@ -76,20 +69,15 @@ module.exports = React.createClass({ var foundMatch = false; while (tempSelectedMention < this.state.selectedMention && this.getSelection(++tempSelectedMention)) { if (this.state.selectedUsername === this.refs['mention' + tempSelectedMention].props.username) { - this.refs['mention' + tempSelectedMention].select(); this.setState({ selectedMention: tempSelectedMention }); foundMatch = true; break; } } if (this.getSelection(0) && !foundMatch) { - this.refs.mention0.select(); this.setState({ selectedMention: 0, selectedUsername: this.refs.mention0.props.username }); } } - else if (this.getSelection(this.state.selectedMention)) { - this.refs['mention' + this.state.selectedMention].select(); - } } else if (this.state.selectedMention !== 0) { this.setState({ selectedMention: 0, selectedUsername: "" }); @@ -114,11 +102,7 @@ module.exports = React.createClass({ this.setState({ mentionText: '-1' }); }, handleMouseEnter: function(listId) { - if (this.getSelection(this.state.selectedMention)) { - this.refs['mention' + this.state.selectedMention].deselect(); - } this.setState({ selectedMention: listId, selectedUsername: this.refs['mention' + listId].props.username }); - this.refs['mention' + listId].select(); }, getSelection: function(listId) { if (!this.refs['mention' + listId]) @@ -127,7 +111,7 @@ module.exports = React.createClass({ return true; }, addCurrentMention: function() { - if (!this.refs['mention' + this.state.selectedMention]) + if (!this.getSelection(this.state.selectedMention)) this.addFirstMention(); else this.refs['mention' + this.state.selectedMention].handleClick(); @@ -169,6 +153,7 @@ module.exports = React.createClass({ return { excludeUsers: [], mentionText: "-1", selectedMention: 0, selectedUsername: "" }; }, render: function() { + var self = this; var mentionText = this.state.mentionText; if (mentionText === '-1') return null; @@ -220,8 +205,8 @@ module.exports = React.createClass({ secondary_text={users[i].secondary_text} id={users[i].id} listId={index} - isFocus={this.state.selectedMention === index ? true : false} - handleMouseEnter={this.handleMouseEnter} + isFocused={this.state.selectedMention === index ? "mentions-focus" : ""} + handleMouseEnter={function(value) { return function() { self.handleMouseEnter(value); } }(index)} handleClick={this.handleClick} /> ); index++; From 1f87c46360b434335ee4b8eae5162b8d37de6f4b Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Thu, 16 Jul 2015 09:27:18 -0700 Subject: [PATCH 53/90] Minor code change for clarity --- web/react/components/mention_list.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index fff0c10b8f..c5ff823461 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -129,7 +129,7 @@ module.exports = React.createClass({ if (direction === "up" && ifLoopUp !== -1) scrollAmount = $("#mentionsbox").height() * 100; //Makes sure that it scrolls all the way to the bottom - else if (direction === "down" && this.refs['mention' + this.state.selectedMention].props.listId === 0) + else if (direction === "down" && this.state.selectedMention === 0) scrollAmount = 0; else if (direction === "up") scrollAmount = "-=" + ($('#'+this.refs['mention' + this.state.selectedMention].props.id +"_mentions").innerHeight() - 5); From 1fe86a004ef2a99c55a583b2b2b0bf1c40b6afe3 Mon Sep 17 00:00:00 2001 From: nickago Date: Thu, 16 Jul 2015 11:26:14 -0700 Subject: [PATCH 54/90] Added table updater --- store/sql_user_store.go | 1 + 1 file changed, 1 insertion(+) diff --git a/store/sql_user_store.go b/store/sql_user_store.go index dd11dd0ae4..665e4d697d 100644 --- a/store/sql_user_store.go +++ b/store/sql_user_store.go @@ -37,6 +37,7 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore { } func (s SqlUserStore) UpgradeSchemaIfNeeded() { + s.CreateColumnIfNotExists("Users","LastPictureUpdate", "LastPasswordUpdate", "bigint(20)", "0") } func (us SqlUserStore) CreateIndexesIfNotExists() { From 43368061d5bfd433c8bbb71fba5da45faf8119d9 Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Fri, 17 Jul 2015 01:30:30 +0500 Subject: [PATCH 55/90] MM-1401 - Removing most inline styles and other UI changes --- web/react/components/channel_header.jsx | 11 ++++---- web/react/components/edit_channel_modal.jsx | 2 +- web/react/components/get_link_modal.jsx | 2 +- web/react/components/member_list_item.jsx | 2 +- web/react/components/member_list_team.jsx | 2 +- web/react/components/new_channel.jsx | 2 +- web/react/components/post_right.jsx | 2 +- web/react/components/sidebar_header.jsx | 2 +- web/react/components/user_profile.jsx | 4 +-- web/react/components/user_settings.jsx | 12 ++++---- web/sass-files/sass/partials/_base.scss | 8 +++++- web/sass-files/sass/partials/_files.scss | 1 + web/sass-files/sass/partials/_headers.scss | 26 ++++++++++++++---- web/sass-files/sass/partials/_modal.scss | 1 + web/sass-files/sass/partials/_popover.scss | 9 ++++++ web/sass-files/sass/partials/_post.scss | 2 +- web/sass-files/sass/partials/_responsive.scss | 4 ++- web/sass-files/sass/partials/_settings.scss | 4 +++ .../sass/partials/_sidebar--right.scss | 8 ++++++ web/sass-files/sass/styles.scss | 3 +- web/static/images/dropdown-icon.png | Bin 2877 -> 0 bytes 21 files changed, 76 insertions(+), 31 deletions(-) create mode 100644 web/sass-files/sass/partials/_popover.scss delete mode 100644 web/static/images/dropdown-icon.png diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx index fe381a59e9..836454b46a 100644 --- a/web/react/components/channel_header.jsx +++ b/web/react/components/channel_header.jsx @@ -53,12 +53,12 @@ var PopoverListMembers = React.createClass({ }); members.forEach(function(m) { - popoverHtml += "
          " + m.username + "
          "; + popoverHtml += "
          " + m.username + "
          "; }); } return ( -
          +
          {count}
          @@ -200,11 +200,10 @@ module.exports = React.createClass({ -
          +
          - - -
          diff --git a/web/react/components/edit_channel_modal.jsx b/web/react/components/edit_channel_modal.jsx index 255654fd52..c0818959ad 100644 --- a/web/react/components/edit_channel_modal.jsx +++ b/web/react/components/edit_channel_modal.jsx @@ -43,7 +43,7 @@ module.exports = React.createClass({

          Edit {this.state.title} Description

          - +
          diff --git a/web/react/components/get_link_modal.jsx b/web/react/components/get_link_modal.jsx index bbfdce63a6..af5314e648 100644 --- a/web/react/components/get_link_modal.jsx +++ b/web/react/components/get_link_modal.jsx @@ -38,7 +38,7 @@ module.exports = React.createClass({

          {"The link below is used for open " + strings.TeamPlural + " or if you allowed your " + strings.Team + " members to sign up using their " + strings.Company + " email addresses."}

          - +
          diff --git a/web/react/components/member_list_item.jsx b/web/react/components/member_list_item.jsx index cf8c71d7e6..a5279909b1 100644 --- a/web/react/components/member_list_item.jsx +++ b/web/react/components/member_list_item.jsx @@ -49,7 +49,7 @@ module.exports = React.createClass({
          ); } else { - invite =
          {member.roles || 'Member'}
          ; + invite =
          {member.roles || 'Member'}
          ; } return ( diff --git a/web/react/components/member_list_team.jsx b/web/react/components/member_list_team.jsx index aa53c5db64..89b5e49d50 100644 --- a/web/react/components/member_list_team.jsx +++ b/web/react/components/member_list_team.jsx @@ -59,7 +59,7 @@ var MemberListTeamItem = React.createClass({ return {}; }, render: function() { - var server_error = this.state.server_error ?
          : null; + var server_error = this.state.server_error ?
          : null; var user = this.props.user; var currentRoles = "Member"; var timestamp = UserStore.getCurrentUser().update_at; diff --git a/web/react/components/new_channel.jsx b/web/react/components/new_channel.jsx index 160241c1c5..069e0d6b1c 100644 --- a/web/react/components/new_channel.jsx +++ b/web/react/components/new_channel.jsx @@ -122,7 +122,7 @@ module.exports = React.createClass({
          - +
          { server_error } diff --git a/web/react/components/post_right.jsx b/web/react/components/post_right.jsx index 408fbf83ac..dca05051dd 100644 --- a/web/react/components/post_right.jsx +++ b/web/react/components/post_right.jsx @@ -43,7 +43,7 @@ RhsHeaderPost = React.createClass({ }); }, render: function() { - var back = this.props.fromSearch ? {"< "} : ""; + var back = this.props.fromSearch ? : ""; return (
          diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx index 54858a04df..83ad4470e2 100644 --- a/web/react/components/sidebar_header.jsx +++ b/web/react/components/sidebar_header.jsx @@ -91,7 +91,7 @@ var NavbarDropdown = React.createClass({
            • Account Settings
            • diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx index 89d0a80ff2..65f0259197 100644 --- a/web/react/components/user_profile.jsx +++ b/web/react/components/user_profile.jsx @@ -53,7 +53,7 @@ module.exports = React.createClass({ var name = this.props.overwriteName ? this.props.overwriteName : this.state.profile.username; - var data_content = ""; + var data_content = ""; if (!config.ShowEmail) { data_content += "
              Email not shared
              "; } else { @@ -61,7 +61,7 @@ module.exports = React.createClass({ } return ( -
              +
              { name }
              ); diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx index 06d8d0208b..17afadce27 100644 --- a/web/react/components/user_settings.jsx +++ b/web/react/components/user_settings.jsx @@ -560,7 +560,7 @@ var AuditTab = React.createClass({

              Activity Log

              -
              +
              @@ -576,11 +576,11 @@ var AuditTab = React.createClass({ this.state.audits.map(function(value, index) { return ( - - - - - + + + + + ); }, this) diff --git a/web/sass-files/sass/partials/_base.scss b/web/sass-files/sass/partials/_base.scss index 8f4ff7b60e..1fb9700753 100644 --- a/web/sass-files/sass/partials/_base.scss +++ b/web/sass-files/sass/partials/_base.scss @@ -10,6 +10,9 @@ body { height: 100%; &.white { background: #fff; + > .container-fluid { + overflow: auto; + } .inner__wrap { > .row.content { min-height: 100%; @@ -53,6 +56,9 @@ div.theme { .form-control { @include border-radius(2px); + &.no-resize { + resize: none; + } } .form-group { @@ -127,7 +133,7 @@ div.theme { } .glyphicon-refresh-animate { - @include animation(spin .7s infinite linear); + @include animation(spin .7s infinite linear); } .black-bg { diff --git a/web/sass-files/sass/partials/_files.scss b/web/sass-files/sass/partials/_files.scss index c584c240d1..56d03e1719 100644 --- a/web/sass-files/sass/partials/_files.scss +++ b/web/sass-files/sass/partials/_files.scss @@ -32,6 +32,7 @@ } } .preview-img { + display: block; height: auto; max-width: 100%; } diff --git a/web/sass-files/sass/partials/_headers.scss b/web/sass-files/sass/partials/_headers.scss index 338f5ceb46..d876d8b37f 100644 --- a/web/sass-files/sass/partials/_headers.scss +++ b/web/sass-files/sass/partials/_headers.scss @@ -95,11 +95,7 @@ } } .dropdown__icon { - background: url("../images/dropdown-icon.png"); - width: 4px; - height: 16px; - @include background-size(100% 100%); - display: inline-block; + fill: #fff; } } .user__picture { @@ -163,7 +159,7 @@ #member_popover { width: 45px; color: #999; - + cursor: pointer; } &.alt { margin: 0; @@ -230,3 +226,21 @@ top: 1px; } } + +.channel-header__links { + height: 32px; + vertical-align: top; + display: inline-block; + width: 15px; + margin: 9px 3px 3px 0; + &:hover { + svg { + fill: #888; + } + } + svg { + vertical-align: top; + margin-top: 8px; + fill: #AAA; + } +} \ No newline at end of file diff --git a/web/sass-files/sass/partials/_modal.scss b/web/sass-files/sass/partials/_modal.scss index 707e71cf0a..1b03388846 100644 --- a/web/sass-files/sass/partials/_modal.scss +++ b/web/sass-files/sass/partials/_modal.scss @@ -154,6 +154,7 @@ background: #FFF; position: relative; max-width: 90%; + min-height: 50px; min-width: 280px; @include border-radius(3px); display: table; diff --git a/web/sass-files/sass/partials/_popover.scss b/web/sass-files/sass/partials/_popover.scss new file mode 100644 index 0000000000..fa1b448419 --- /dev/null +++ b/web/sass-files/sass/partials/_popover.scss @@ -0,0 +1,9 @@ +.user-popover { + cursor: pointer; + display: inline-block; +} + +.user-popover__image { + margin: 0 0 10px; + @include border-radius(128px); +} \ No newline at end of file diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss index 40ed40b49a..465c50296a 100644 --- a/web/sass-files/sass/partials/_post.scss +++ b/web/sass-files/sass/partials/_post.scss @@ -425,4 +425,4 @@ body.ios { width: 40px; } } -} +} \ No newline at end of file diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss index e017392406..1f96431750 100644 --- a/web/sass-files/sass/partials/_responsive.scss +++ b/web/sass-files/sass/partials/_responsive.scss @@ -413,7 +413,7 @@ } } .footer, .footer-pane, .footer-push { - height: 185px; + height: auto; } .footer-pane { .footer-link { @@ -574,6 +574,8 @@ .modal { .modal-image { .image-wrapper { + font-size: 12px; + max-width: 280px; .modal-close { @include opacity(1); } diff --git a/web/sass-files/sass/partials/_settings.scss b/web/sass-files/sass/partials/_settings.scss index af759c650b..e60bc290ec 100644 --- a/web/sass-files/sass/partials/_settings.scss +++ b/web/sass-files/sass/partials/_settings.scss @@ -1,6 +1,10 @@ .user-settings { background: #fff; min-height:300px; + .table-responsive { + max-width: 560px; + max-height: 300px; + } } .settings-modal { diff --git a/web/sass-files/sass/partials/_sidebar--right.scss b/web/sass-files/sass/partials/_sidebar--right.scss index a0e82fd2f4..d02a924489 100644 --- a/web/sass-files/sass/partials/_sidebar--right.scss +++ b/web/sass-files/sass/partials/_sidebar--right.scss @@ -10,6 +10,14 @@ &.move--left { right: 0; } + .sidebar--right__back { + color: #666; + width: 20px; + text-align: center; + margin: 0 0 0 -6px; + font-size: 12px; + display: inline-block; + } .sidebar-right__body { border-left: $border-gray; border-top: $border-gray; diff --git a/web/sass-files/sass/styles.scss b/web/sass-files/sass/styles.scss index 9cc26320c2..294f6122af 100644 --- a/web/sass-files/sass/styles.scss +++ b/web/sass-files/sass/styles.scss @@ -15,6 +15,7 @@ @import "partials/headers"; @import "partials/footer"; @import "partials/content"; +@import "partials/popover"; @import "partials/post"; @import "partials/post_right"; @import "partials/navbar"; @@ -29,7 +30,7 @@ @import "partials/modal"; @import "partials/mentions"; @import "partials/error"; -@import "partials/loading"; +@import "partials/loading"; // Responsive Css @import "partials/responsive"; diff --git a/web/static/images/dropdown-icon.png b/web/static/images/dropdown-icon.png deleted file mode 100644 index 5c271cfc78662831000aaf5d96792117bb21b2f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2877 zcmV-D3&Qk?P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0001JNklVk5CaDa_%6`V{5J6Vp3oukA#nx|84)8n@jCFzsHl@#YL6-& zs7+lwvZ#%u|51b5fe+_wBR7n|@8bSNoi9qx Date: Thu, 16 Jul 2015 13:40:18 -0700 Subject: [PATCH 56/90] Moved admin checking into seperate function --- api/context.go | 10 ++++++++++ api/post.go | 20 +++++++------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/api/context.go b/api/context.go index bea0fbeff4..0c9dee5c33 100644 --- a/api/context.go +++ b/api/context.go @@ -265,6 +265,16 @@ func (c *Context) IsSystemAdmin() bool { return false } +func (c *Context) IsTeamAdmin() bool { + if uresult := <-Srv.Store.User().Get(c.Session.UserId); uresult.Err != nil { + c.Err = uresult.Err + return false + } else { + user := uresult.Data.(*model.User) + return strings.Contains(user.Roles, model.ROLE_ADMIN) && user.TeamId == c.Session.TeamId + } +} + func (c *Context) RemoveSessionCookie(w http.ResponseWriter) { sessionCache.Remove(c.Session.Id) diff --git a/api/post.go b/api/post.go index 0a8b5a20bb..214429bb91 100644 --- a/api/post.go +++ b/api/post.go @@ -619,23 +619,17 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId) pchan := Srv.Store.Post().Get(postId) - uchan := Srv.Store.User().Get(c.Session.UserId) - if uresult := <-uchan; uresult.Err != nil { - c.Err = uresult.Err + if !c.HasPermissionsToChannel(cchan, "deletePost") && !c.IsTeamAdmin(){ return - } else if presult := <-pchan; presult.Err != nil { - c.Err = presult.Err + } + + if result := <-pchan; result.Err != nil { + c.Err = result.Err return } else { - user := uresult.Data.(*model.User) - - if !c.HasPermissionsToChannel(cchan, "deletePost") && !strings.Contains(user.Roles,"admin"){ - return - } - - post := presult.Data.(*model.PostList).Posts[postId] + post := result.Data.(*model.PostList).Posts[postId] if post == nil { c.SetInvalidParam("deletePost", "postId") @@ -648,7 +642,7 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { return } - if post.UserId != c.Session.UserId && !strings.Contains(user.Roles,"admin") { + if post.UserId != c.Session.UserId && !strings.Contains(c.Session.Roles,model.ROLE_ADMIN) { c.Err = model.NewAppError("deletePost", "You do not have the appropriate permissions", "") c.Err.StatusCode = http.StatusForbidden return From 41d2786e3e256acf22be18c96c036e84e0ae4fc9 Mon Sep 17 00:00:00 2001 From: nickago Date: Thu, 16 Jul 2015 13:50:20 -0700 Subject: [PATCH 57/90] reworked logic to stem from post --- api/context.go | 4 ++-- api/post.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/context.go b/api/context.go index 0c9dee5c33..9f23d71a0c 100644 --- a/api/context.go +++ b/api/context.go @@ -265,8 +265,8 @@ func (c *Context) IsSystemAdmin() bool { return false } -func (c *Context) IsTeamAdmin() bool { - if uresult := <-Srv.Store.User().Get(c.Session.UserId); uresult.Err != nil { +func (c *Context) IsTeamAdmin(userId string) bool { + if uresult := <-Srv.Store.User().Get(userId); uresult.Err != nil { c.Err = uresult.Err return false } else { diff --git a/api/post.go b/api/post.go index 214429bb91..36fcafd392 100644 --- a/api/post.go +++ b/api/post.go @@ -620,10 +620,6 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId) pchan := Srv.Store.Post().Get(postId) - if !c.HasPermissionsToChannel(cchan, "deletePost") && !c.IsTeamAdmin(){ - return - } - if result := <-pchan; result.Err != nil { c.Err = result.Err return @@ -631,6 +627,10 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { post := result.Data.(*model.PostList).Posts[postId] + if !c.HasPermissionsToChannel(cchan, "deletePost") && !c.IsTeamAdmin(post.UserId){ + return + } + if post == nil { c.SetInvalidParam("deletePost", "postId") return From 19a34f312b91bd4575d3035ed5aa54369f392d79 Mon Sep 17 00:00:00 2001 From: nickago Date: Thu, 16 Jul 2015 14:22:15 -0700 Subject: [PATCH 58/90] Fixed config on our link parser --- web/react/utils/utils.jsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 19c074606f..ec2b7e7e88 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -198,7 +198,13 @@ module.exports.getTimestamp = function() { } var testUrlMatch = function(text) { - var urlMatcher = new Autolinker.matchParser.MatchParser; + var urlMatcher = new Autolinker.matchParser.MatchParser({ + urls: true, + emails: false, + twitter: false, + phone: false, + hashtag: false, + }); var result = []; var replaceFn = function(match) { var linkData = {}; @@ -417,7 +423,6 @@ module.exports.textToJsx = function(text, options) { highlightSearchClass = " search-highlight"; } - if (explicitMention && (UserStore.getProfileByUsername(explicitMention[1]) || Constants.SPECIAL_MENTIONS.indexOf(explicitMention[1]) !== -1)) From f99743bfa08c8c3c4a5264cf30c814d145a2e90f Mon Sep 17 00:00:00 2001 From: nickago Date: Thu, 16 Jul 2015 14:23:27 -0700 Subject: [PATCH 59/90] readded space --- web/react/utils/utils.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index ec2b7e7e88..81de072308 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -423,6 +423,7 @@ module.exports.textToJsx = function(text, options) { highlightSearchClass = " search-highlight"; } + if (explicitMention && (UserStore.getProfileByUsername(explicitMention[1]) || Constants.SPECIAL_MENTIONS.indexOf(explicitMention[1]) !== -1)) From 5da579cbcc374a67487337c309b189b67c0a788f Mon Sep 17 00:00:00 2001 From: nickago Date: Thu, 16 Jul 2015 14:24:15 -0700 Subject: [PATCH 60/90] fixed formatting --- web/react/utils/utils.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 81de072308..0d84a49835 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -423,7 +423,7 @@ module.exports.textToJsx = function(text, options) { highlightSearchClass = " search-highlight"; } - + if (explicitMention && (UserStore.getProfileByUsername(explicitMention[1]) || Constants.SPECIAL_MENTIONS.indexOf(explicitMention[1]) !== -1)) From f0841af393d1d0c48bf27b68fca3be8fde096dd6 Mon Sep 17 00:00:00 2001 From: nickago Date: Thu, 16 Jul 2015 14:47:28 -0700 Subject: [PATCH 61/90] Check your credentials from session --- api/context.go | 2 +- api/post_test.go | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/api/context.go b/api/context.go index 9f23d71a0c..054e42e2ed 100644 --- a/api/context.go +++ b/api/context.go @@ -271,7 +271,7 @@ func (c *Context) IsTeamAdmin(userId string) bool { return false } else { user := uresult.Data.(*model.User) - return strings.Contains(user.Roles, model.ROLE_ADMIN) && user.TeamId == c.Session.TeamId + return strings.Contains(c.Session.Roles, model.ROLE_ADMIN) && user.TeamId == c.Session.TeamId } } diff --git a/api/post_test.go b/api/post_test.go index 5009ff54d1..37798a0c93 100644 --- a/api/post_test.go +++ b/api/post_test.go @@ -10,6 +10,7 @@ import ( "net/http" "testing" "time" + "fmt" ) func TestCreatePost(t *testing.T) { @@ -534,6 +535,10 @@ func TestDeletePosts(t *testing.T) { Client.LoginByEmail(team.Domain, userAdmin.Email, "pwd") + fmt.Println(userAdmin.Email) + fmt.Println(team.Email) + fmt.Println(userAdmin.Roles) + Client.Must(Client.DeletePost(channel1.Id, post4.Id)) } From 830430370340d419835246404a9ac353764eb5b3 Mon Sep 17 00:00:00 2001 From: nickago Date: Thu, 16 Jul 2015 14:48:40 -0700 Subject: [PATCH 62/90] removed testing material --- api/post_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/api/post_test.go b/api/post_test.go index 37798a0c93..c62b4423e6 100644 --- a/api/post_test.go +++ b/api/post_test.go @@ -535,10 +535,6 @@ func TestDeletePosts(t *testing.T) { Client.LoginByEmail(team.Domain, userAdmin.Email, "pwd") - fmt.Println(userAdmin.Email) - fmt.Println(team.Email) - fmt.Println(userAdmin.Roles) - Client.Must(Client.DeletePost(channel1.Id, post4.Id)) } From 372869354f8c4480f39c67348694ca2192747b6d Mon Sep 17 00:00:00 2001 From: nickago Date: Thu, 16 Jul 2015 14:49:15 -0700 Subject: [PATCH 63/90] removed testing material --- api/post_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/api/post_test.go b/api/post_test.go index c62b4423e6..5009ff54d1 100644 --- a/api/post_test.go +++ b/api/post_test.go @@ -10,7 +10,6 @@ import ( "net/http" "testing" "time" - "fmt" ) func TestCreatePost(t *testing.T) { From 4329437ea690fc07f3727325c54a349eada8aa11 Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Fri, 17 Jul 2015 03:53:58 +0500 Subject: [PATCH 64/90] Improving search header for mobile layouts and other UI changes --- web/react/components/search_bar.jsx | 2 +- web/sass-files/sass/partials/_headers.scss | 7 ++++++- web/sass-files/sass/partials/_modal.scss | 2 +- web/sass-files/sass/partials/_responsive.scss | 9 ++++++--- web/sass-files/sass/partials/_search.scss | 13 ++++++------- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/web/react/components/search_bar.jsx b/web/react/components/search_bar.jsx index ab7e99d609..f21f0cd588 100644 --- a/web/react/components/search_bar.jsx +++ b/web/react/components/search_bar.jsx @@ -92,7 +92,7 @@ module.exports = React.createClass({ render: function() { return (
              -
              +
              Cancel
              Date: Fri, 17 Jul 2015 10:03:33 -0400 Subject: [PATCH 65/90] Modifications to readme for mm-1356 --- README.md | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index a4f95dbdf8..aff0d12887 100644 --- a/README.md +++ b/README.md @@ -108,26 +108,10 @@ AWS Elastic Beanstalk Setup (Docker) 11. You can set the configuration details as you please but they may be left at their defaults. When you are done press next. 12. Environment tags my be left blank. Press next. 13. You will be asked to review your information. Press Launch. - 14. Up near the top of the dashboard you will see a domain of the form \*.elasticbeanstalk.com copy this as you will need it later. - -2. Map a wildcard domain to the new elastic beanstalk application - 15. From the AWS console select route 53 - 16. From the sidebar select Hosted Zones - 17. Select the domain you want to use or create a new one. - 18. Modify an existing CNAME record set or create a new one with the name * and the value of the domain you copied in step 1.13. - 19. Save the record set - -3. Set the environment variable "MATTERMOST\_DOMAIN" to the domain you mapped above (example.com not www.example.com) - 20. Return the Elastic Beanstalk from the AWS console. - 21. Select the environment you created. - 22. Select configuration from the sidebar. - 23. Click the gear beside software configuration. - 24. Add an environment property with the name “MATTERMOST\_DOMAIN” and a value of the domain you mapped in route 53. For example if your domain is \*.example.com you would enter example.com not www.example.com. - 25. Select apply. 4. Try it out! - 26. Return to the dashboard on the sidebar and wait for beanstalk update the environment. - 27. Try it out by entering the domain you mapped into your browser. + 14. Wait for beanstalk update the environment. + 15. Try it out by entering the domain of the form \*.elasticbeanstalk.com found at the top of the dashboard into your browser. You can also map your own domain if you wish. Contributing ------------ From 09b2fda4f61cb5769202a12b850b42901959ee9f Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Fri, 17 Jul 2015 10:24:43 -0400 Subject: [PATCH 66/90] Fixing grammar --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aff0d12887..65a3e7c663 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ AWS Elastic Beanstalk Setup (Docker) 13. You will be asked to review your information. Press Launch. 4. Try it out! - 14. Wait for beanstalk update the environment. + 14. Wait for beanstalk to update the environment. 15. Try it out by entering the domain of the form \*.elasticbeanstalk.com found at the top of the dashboard into your browser. You can also map your own domain if you wish. Contributing From bb639518a05ac4a6e45b7e795cfdc5ff45ebf369 Mon Sep 17 00:00:00 2001 From: nickago Date: Fri, 17 Jul 2015 07:27:37 -0700 Subject: [PATCH 67/90] Fixed typo --- api/templates/reset_body.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/templates/reset_body.html b/api/templates/reset_body.html index 3a5d62ab48..4b59766637 100644 --- a/api/templates/reset_body.html +++ b/api/templates/reset_body.html @@ -18,7 +18,7 @@
              { new Date(value.create_at).toLocaleString() }{ value.action.replace("/api/v1", "") }{ value.ip_address }{ value.session_id }{ value.extra_info }{ new Date(value.create_at).toLocaleString() }{ value.action.replace("/api/v1", "") }{ value.ip_address }{ value.session_id }{ value.extra_info }

              You requested a password reset

              -

              To change your password, click below.
              If you did not mean to reset your password, please ignore this email and your password will remain the same.

              +

              To change your password, click "Reset Password" below.
              If you did not mean to reset your password, please ignore this email and your password will remain the same.

              Reset Password

              From 8ead147ebc2c56705c1896586782553dcb30e271 Mon Sep 17 00:00:00 2001 From: nickago Date: Fri, 17 Jul 2015 07:52:56 -0700 Subject: [PATCH 68/90] Changed javascript to CSS method --- web/react/components/post_info.jsx | 4 +--- web/react/components/post_right.jsx | 4 +--- web/sass-files/sass/partials/_post.scss | 6 ++++++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx index cf01747f00..17746655db 100644 --- a/web/react/components/post_info.jsx +++ b/web/react/components/post_info.jsx @@ -30,9 +30,7 @@ module.exports = React.createClass({
              { isOwner || (this.props.allowReply === "true" && type != "Comment") ?
              - +
              • Edit
              • : "" } diff --git a/web/react/components/post_right.jsx b/web/react/components/post_right.jsx index dca05051dd..cc6751738e 100644 --- a/web/react/components/post_right.jsx +++ b/web/react/components/post_right.jsx @@ -129,9 +129,7 @@ RootPost = React.createClass({
                { isOwner ?
                - +
                • Edit
                • Delete
                • diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss index 465c50296a..9368786d1a 100644 --- a/web/sass-files/sass/partials/_post.scss +++ b/web/sass-files/sass/partials/_post.scss @@ -208,6 +208,12 @@ body.ios { .dropdown, .comment-icon__container { @include opacity(1); } + .dropdown-toggle:after { + content: '...'; + } + .dropdown-toggle:hover:after { + content: '[...]'; + } } background: #f5f5f5; } From af56bed1a33cc4b5af696b04e156f23dbd25a369 Mon Sep 17 00:00:00 2001 From: nickago Date: Fri, 17 Jul 2015 12:09:12 -0700 Subject: [PATCH 69/90] Even on error, submission stop, updated to relect that --- web/react/components/create_comment.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx index 9e3feb25c2..6ed0f0b349 100644 --- a/web/react/components/create_comment.jsx +++ b/web/react/components/create_comment.jsx @@ -43,7 +43,7 @@ module.exports = React.createClass({ client.createPost(post, ChannelStore.getCurrent(), function(data) { - this.setState({ messageText: '', submitting: false, post_error: null }); + this.setState({ messageText: '', submitting: false, post_error: null, server_error: null }); this.clearPreviews(); AsyncClient.getPosts(true, this.props.channelId); @@ -57,6 +57,7 @@ module.exports = React.createClass({ function(err) { var state = {}; state.server_error = err.message; + state.submitting = false; if (err.message === "Invalid RootId parameter") { if ($('#post_deleted').length > 0) $('#post_deleted').modal('show'); From c09f1b9e4e5638080622ff9aa70735db382a16df Mon Sep 17 00:00:00 2001 From: hmhealey Date: Thu, 9 Jul 2015 13:59:19 -0400 Subject: [PATCH 70/90] Renamed FullName column in database to Nickname. Renamed all serverside references from FullName to Nickname. --- api/api_test.go | 2 +- api/auto_users.go | 4 +-- api/channel_benchmark_test.go | 2 +- api/channel_test.go | 56 ++++++++++++++--------------- api/command_test.go | 8 ++--- api/file_test.go | 10 +++--- api/post.go | 6 ++-- api/post_test.go | 32 ++++++++--------- api/team.go | 4 +-- api/team_test.go | 24 ++++++------- api/user.go | 8 ++--- api/user_test.go | 64 ++++++++++++++++----------------- api/web_socket_test.go | 4 +-- manualtesting/manual_testing.go | 2 +- model/channel_extra.go | 4 +-- model/user.go | 10 +++--- model/user_test.go | 4 +-- store/sql_channel_store.go | 8 ++--- store/sql_channel_store_test.go | 4 +-- store/sql_store.go | 53 ++++++++++++++------------- store/sql_user_store.go | 11 ++++-- web/web.go | 6 ++-- 22 files changed, 168 insertions(+), 158 deletions(-) diff --git a/api/api_test.go b/api/api_test.go index b407c2a5e2..7d2faed4fc 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -26,7 +26,7 @@ func SetupBenchmark() (*model.Team, *model.User, *model.Channel) { team := &model.Team{Name: "Benchmark Team", Domain: "z-z-" + model.NewId() + "a", Email: "benchmark@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "benchmark@test.com", FullName: "Mr. Benchmarker", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: model.NewId() + "benchmark@test.com", Nickname: "Mr. Benchmarker", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) Client.LoginByEmail(team.Domain, user.Email, "pwd") diff --git a/api/auto_users.go b/api/auto_users.go index 493ec5c17d..d6918f13ac 100644 --- a/api/auto_users.go +++ b/api/auto_users.go @@ -41,7 +41,7 @@ func CreateBasicUser(client *model.Client) *model.AppError { return err } basicteam := result.Data.(*model.Team) - newuser := &model.User{TeamId: basicteam.Id, Email: BTEST_USER_EMAIL, FullName: BTEST_USER_NAME, Password: BTEST_USER_PASSWORD} + newuser := &model.User{TeamId: basicteam.Id, Email: BTEST_USER_EMAIL, Nickname: BTEST_USER_NAME, Password: BTEST_USER_PASSWORD} result, err = client.CreateUser(newuser, "") if err != nil { return err @@ -65,7 +65,7 @@ func (cfg *AutoUserCreator) createRandomUser() (*model.User, bool) { user := &model.User{ TeamId: cfg.teamID, Email: userEmail, - FullName: userName, + Nickname: userName, Password: USER_PASSWORD} result, err := cfg.client.CreateUser(user, "") diff --git a/api/channel_benchmark_test.go b/api/channel_benchmark_test.go index 461a7ed3a0..17d3deb274 100644 --- a/api/channel_benchmark_test.go +++ b/api/channel_benchmark_test.go @@ -138,7 +138,7 @@ func BenchmarkJoinChannel(b *testing.B) { } // Secondary test user to join channels created by primary test user - user := &model.User{TeamId: team.Id, Email: model.NewId() + "random@test.com", FullName: "That Guy", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: model.NewId() + "random@test.com", Nickname: "That Guy", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) Client.LoginByEmail(team.Domain, user.Email, "pwd") diff --git a/api/channel_test.go b/api/channel_test.go index ed05546939..31ab85117c 100644 --- a/api/channel_test.go +++ b/api/channel_test.go @@ -20,7 +20,7 @@ func TestCreateChannel(t *testing.T) { team2 := &model.Team{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -97,11 +97,11 @@ func TestCreateDirectChannel(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) @@ -152,15 +152,15 @@ func TestUpdateChannel(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, FullName: "Corey Hulen", Password: "pwd"} + userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"} userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(userTeamAdmin.Id)) - userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id)) - userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(userStd.Id)) userStd.Roles = "" @@ -223,7 +223,7 @@ func TestUpdateChannelDesc(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -266,7 +266,7 @@ func TestUpdateChannelDesc(t *testing.T) { t.Fatal("should have errored on bad channel desc") } - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) @@ -285,7 +285,7 @@ func TestGetChannel(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -327,7 +327,7 @@ func TestGetMoreChannel(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) @@ -339,7 +339,7 @@ func TestGetMoreChannel(t *testing.T) { channel2 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) @@ -371,7 +371,7 @@ func TestJoinChannel(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) @@ -383,7 +383,7 @@ func TestJoinChannel(t *testing.T) { channel3 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) @@ -399,7 +399,7 @@ func TestJoinChannel(t *testing.T) { data["user_id"] = user1.Id rchannel := Client.Must(Client.CreateDirectChannel(data)).Data.(*model.Channel) - user3 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user3 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) Client.LoginByEmail(team.Domain, user3.Email, "pwd") @@ -415,7 +415,7 @@ func TestLeaveChannel(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) @@ -427,7 +427,7 @@ func TestLeaveChannel(t *testing.T) { channel3 := &model.Channel{DisplayName: "B Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_PRIVATE, TeamId: team.Id} channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) @@ -464,11 +464,11 @@ func TestDeleteChannel(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, FullName: "Corey Hulen", Password: "pwd"} + userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"} userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(userTeamAdmin.Id)) - userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id)) @@ -500,7 +500,7 @@ func TestDeleteChannel(t *testing.T) { t.Fatal("should have failed to post to deleted channel") } - userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(userStd.Id)) @@ -534,7 +534,7 @@ func TestGetChannelExtraInfo(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -555,7 +555,7 @@ func TestAddChannelMember(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) @@ -564,7 +564,7 @@ func TestAddChannelMember(t *testing.T) { channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) @@ -613,11 +613,11 @@ func TestRemoveChannelMember(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, FullName: "Corey Hulen", Password: "pwd"} + userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"} userTeamAdmin = Client.Must(Client.CreateUser(userTeamAdmin, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(userTeamAdmin.Id)) - userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + userChannelAdmin := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id)) @@ -633,7 +633,7 @@ func TestRemoveChannelMember(t *testing.T) { channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) - userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + userStd := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(userStd.Id)) @@ -683,7 +683,7 @@ func TestUpdateNotifyLevel(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -746,7 +746,7 @@ func TestUpdateNotifyLevel(t *testing.T) { t.Fatal("Should have errored - bad notify level") } - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) Client.LoginByEmail(team.Domain, user2.Email, "pwd") @@ -765,7 +765,7 @@ func TestFuzzyChannel(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) diff --git a/api/command_test.go b/api/command_test.go index 624c445e68..d9912f9d8b 100644 --- a/api/command_test.go +++ b/api/command_test.go @@ -15,7 +15,7 @@ func TestSuggestRootCommands(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) @@ -58,7 +58,7 @@ func TestLogoutCommands(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) @@ -76,7 +76,7 @@ func TestJoinCommands(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) @@ -90,7 +90,7 @@ func TestJoinCommands(t *testing.T) { channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) Client.Must(Client.LeaveChannel(channel2.Id)) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) diff --git a/api/file_test.go b/api/file_test.go index e334fd6e50..044cad921d 100644 --- a/api/file_test.go +++ b/api/file_test.go @@ -27,7 +27,7 @@ func TestUploadFile(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) @@ -114,7 +114,7 @@ func TestGetFile(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) @@ -176,7 +176,7 @@ func TestGetFile(t *testing.T) { team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) - user2 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) @@ -261,11 +261,11 @@ func TestGetPublicLink(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) diff --git a/api/post.go b/api/post.go index efca2f570a..ab89133d56 100644 --- a/api/post.go +++ b/api/post.go @@ -301,7 +301,7 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) { // If turned on, add the user's case sensitive first name if profile.NotifyProps["first_name"] == "true" { - splitName := strings.Split(profile.FullName, " ") + splitName := strings.Split(profile.Nickname, " ") if len(splitName) > 0 && splitName[0] != "" { keywordMap[splitName[0]] = append(keywordMap[splitName[0]], profile.Id) } @@ -395,10 +395,10 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) { continue } - firstName := strings.Split(profileMap[id].FullName, " ")[0] + firstName := strings.Split(profileMap[id].Nickname, " ")[0] bodyPage := NewServerTemplatePage("post_body", teamUrl) - bodyPage.Props["FullName"] = firstName + bodyPage.Props["Nickname"] = firstName bodyPage.Props["TeamName"] = teamName bodyPage.Props["ChannelName"] = channelName bodyPage.Props["BodyText"] = bodyText diff --git a/api/post_test.go b/api/post_test.go index 5009ff54d1..583d1be43d 100644 --- a/api/post_test.go +++ b/api/post_test.go @@ -21,11 +21,11 @@ func TestCreatePost(t *testing.T) { team2 := &model.Team{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) @@ -103,7 +103,7 @@ func TestCreatePost(t *testing.T) { t.Fatal("Should have been forbidden") } - user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user3.Id)) @@ -132,11 +132,11 @@ func TestCreateValetPost(t *testing.T) { team2 := &model.Team{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) @@ -188,7 +188,7 @@ func TestCreateValetPost(t *testing.T) { t.Fatal(err) } - user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user3.Id)) @@ -224,11 +224,11 @@ func TestUpdatePost(t *testing.T) { team2 := &model.Team{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) @@ -292,7 +292,7 @@ func TestGetPosts(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) @@ -357,7 +357,7 @@ func TestSearchPosts(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) @@ -403,7 +403,7 @@ func TestSearchHashtagPosts(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) @@ -434,7 +434,7 @@ func TestGetPostsCache(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) @@ -483,11 +483,11 @@ func TestDeletePosts(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - userAdmin := &model.User{TeamId: team.Id, Email: team.Email, FullName: "Corey Hulen", Password: "pwd"} + userAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"} userAdmin = Client.Must(Client.CreateUser(userAdmin, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(userAdmin.Id)) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) @@ -543,7 +543,7 @@ func TestEmailMention(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: "corey@test.com", FullName: "Bob Bobby", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: "corey@test.com", Nickname: "Bob Bobby", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) @@ -565,7 +565,7 @@ func TestFuzzyPosts(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) diff --git a/api/team.go b/api/team.go index f9aeecd7e0..dc7804b4d4 100644 --- a/api/team.go +++ b/api/team.go @@ -488,10 +488,10 @@ func InviteMembers(team *model.Team, user *model.User, invites []string) { } sender := "" - if len(strings.TrimSpace(user.FullName)) == 0 { + if len(strings.TrimSpace(user.Nickname)) == 0 { sender = user.Username } else { - sender = user.FullName + sender = user.Nickname } senderRole := "" diff --git a/api/team_test.go b/api/team_test.go index 2bf3219e4c..f61babe8e6 100644 --- a/api/team_test.go +++ b/api/team_test.go @@ -33,7 +33,7 @@ func TestCreateFromSignupTeam(t *testing.T) { hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt)) team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} - user := model.User{Email: props["email"], FullName: "Corey Hulen", Password: "hello"} + user := model.User{Email: props["email"], Nickname: "Corey Hulen", Password: "hello"} ts := model.TeamSignup{Team: team, User: user, Invites: []string{"corey@test.com"}, Data: data, Hash: hash} @@ -77,7 +77,7 @@ func TestCreateTeam(t *testing.T) { t.Fatal(err) } - user := &model.User{TeamId: rteam.Data.(*model.Team).Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: rteam.Data.(*model.Team).Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -114,7 +114,7 @@ func TestFindTeamByEmail(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -142,7 +142,7 @@ func TestFindTeamByDomain(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -182,7 +182,7 @@ func TestFindTeamByEmailSend(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -206,7 +206,7 @@ func TestInviteMembers(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -235,11 +235,11 @@ func TestUpdateTeamName(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) @@ -310,7 +310,7 @@ func TestGetMyTeam(t *testing.T) { team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} ruser, _ := Client.CreateUser(&user, "") store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) @@ -337,18 +337,18 @@ func TestUpdateValetFeature(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) - user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user3.Id)) diff --git a/api/user.go b/api/user.go index 3c0062f8c1..48a9f0da2d 100644 --- a/api/user.go +++ b/api/user.go @@ -181,14 +181,14 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User { l4g.Error("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", ruser.Id, ruser.TeamId, err) } - //fireAndForgetWelcomeEmail(strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl+"/channels/town-square") + //fireAndForgetWelcomeEmail(strings.Split(ruser.Nickname, " ")[0], ruser.Email, team.Name, c.TeamUrl+"/channels/town-square") if user.EmailVerified { if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { l4g.Error("Failed to set email verified err=%v", cresult.Err) } } else { - FireAndForgetVerifyEmail(result.Data.(*model.User).Id, strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl) + FireAndForgetVerifyEmail(result.Data.(*model.User).Id, strings.Split(ruser.Nickname, " ")[0], ruser.Email, team.Name, c.TeamUrl) } ruser.Sanitize(map[string]bool{}) @@ -207,7 +207,7 @@ func fireAndForgetWelcomeEmail(name, email, teamName, link string) { subjectPage := NewServerTemplatePage("welcome_subject", link) bodyPage := NewServerTemplatePage("welcome_body", link) - bodyPage.Props["FullName"] = name + bodyPage.Props["Nickname"] = name bodyPage.Props["TeamName"] = teamName bodyPage.Props["FeedbackName"] = utils.Cfg.EmailSettings.FeedbackName @@ -226,7 +226,7 @@ func FireAndForgetVerifyEmail(userId, name, email, teamName, teamUrl string) { subjectPage := NewServerTemplatePage("verify_subject", teamUrl) subjectPage.Props["TeamName"] = teamName bodyPage := NewServerTemplatePage("verify_body", teamUrl) - bodyPage.Props["FullName"] = name + bodyPage.Props["Nickname"] = name bodyPage.Props["TeamName"] = teamName bodyPage.Props["VerifyUrl"] = link diff --git a/api/user_test.go b/api/user_test.go index 6e99ab9307..edbef7c9ab 100644 --- a/api/user_test.go +++ b/api/user_test.go @@ -28,15 +28,15 @@ func TestCreateUser(t *testing.T) { team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "hello"} + user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "hello"} ruser, err := Client.CreateUser(&user, "") if err != nil { t.Fatal(err) } - if ruser.Data.(*model.User).FullName != user.FullName { - t.Fatal("full name didn't match") + if ruser.Data.(*model.User).Nickname != user.Nickname { + t.Fatal("nickname didn't match") } if ruser.Data.(*model.User).Password != "" { @@ -61,7 +61,7 @@ func TestCreateUser(t *testing.T) { } } - user2 := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "hello", Username: model.BOT_USERNAME} + user2 := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "hello", Username: model.BOT_USERNAME} if _, err := Client.CreateUser(&user2, ""); err == nil { t.Fatal("Should have failed using reserved bot name") @@ -78,7 +78,7 @@ func TestCreateUserAllowedDomains(t *testing.T) { team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_INVITE, AllowedDomains: "spinpunch.com, @nowh.com,@hello.com"} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "hello"} + user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "hello"} _, err := Client.CreateUser(&user, "") if err == nil { @@ -98,7 +98,7 @@ func TestLogin(t *testing.T) { team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} ruser, _ := Client.CreateUser(&user, "") store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) @@ -138,7 +138,7 @@ func TestLogin(t *testing.T) { team2 := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_INVITE} rteam2 := Client.Must(Client.CreateTeam(&team2)) - user2 := model.User{TeamId: rteam2.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := model.User{TeamId: rteam2.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} if _, err := Client.CreateUserFromSignup(&user2, "junk", "1231312"); err == nil { t.Fatal("Should have errored, signed up without hashed email") @@ -167,7 +167,7 @@ func TestSessions(t *testing.T) { team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) @@ -224,11 +224,11 @@ func TestGetUser(t *testing.T) { team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} ruser, _ := Client.CreateUser(&user, "") store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) - user2 := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} ruser2, _ := Client.CreateUser(&user2, "") store.Must(Srv.Store.User().VerifyEmail(ruser2.Data.(*model.User).Id)) @@ -295,7 +295,7 @@ func TestGetAudits(t *testing.T) { team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} ruser, _ := Client.CreateUser(&user, "") store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) @@ -348,7 +348,7 @@ func TestUserCreateImage(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -364,7 +364,7 @@ func TestUserUploadProfileImage(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -463,7 +463,7 @@ func TestUserUpdate(t *testing.T) { time1 := model.GetMillis() - user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd", LastActivityAt: time1, LastPingAt: time1, Roles: ""} + user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd", LastActivityAt: time1, LastPingAt: time1, Roles: ""} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -479,7 +479,7 @@ func TestUserUpdate(t *testing.T) { time.Sleep(100 * time.Millisecond) - user.FullName = "Jim Jimmy" + user.Nickname = "Jim Jimmy" user.TeamId = "12345678901234567890123456" user.LastActivityAt = time2 user.LastPingAt = time2 @@ -489,8 +489,8 @@ func TestUserUpdate(t *testing.T) { if result, err := Client.UpdateUser(user); err != nil { t.Fatal(err) } else { - if result.Data.(*model.User).FullName != "Jim Jimmy" { - t.Fatal("FullName did not update properly") + if result.Data.(*model.User).Nickname != "Jim Jimmy" { + t.Fatal("Nickname did not update properly") } if result.Data.(*model.User).TeamId != team.Id { t.Fatal("TeamId should not have updated") @@ -519,13 +519,13 @@ func TestUserUpdate(t *testing.T) { t.Fatal("Should have errored") } - user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) Client.LoginByEmail(team.Domain, user2.Email, "pwd") - user.FullName = "Tim Timmy" + user.Nickname = "Tim Timmy" if _, err := Client.UpdateUser(user); err == nil { t.Fatal("Should have errored") @@ -538,7 +538,7 @@ func TestUserUpdatePassword(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -581,7 +581,7 @@ func TestUserUpdatePassword(t *testing.T) { t.Fatal(err) } - user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) Client.LoginByEmail(team.Domain, user2.Email, "pwd") @@ -597,11 +597,11 @@ func TestUserUpdateRoles(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) @@ -622,7 +622,7 @@ func TestUserUpdateRoles(t *testing.T) { team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) - user3 := &model.User{TeamId: team2.Id, Email: "test@nowhere.com", FullName: "Corey Hulen", Password: "pwd"} + user3 := &model.User{TeamId: team2.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user3.Id)) @@ -666,11 +666,11 @@ func TestUserUpdateActive(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) @@ -687,7 +687,7 @@ func TestUserUpdateActive(t *testing.T) { team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) - user3 := &model.User{TeamId: team2.Id, Email: "test@nowhere.com", FullName: "Corey Hulen", Password: "pwd"} + user3 := &model.User{TeamId: team2.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user3.Id)) @@ -730,7 +730,7 @@ func TestSendPasswordReset(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -770,7 +770,7 @@ func TestResetPassword(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -854,7 +854,7 @@ func TestUserUpdateNotify(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd", Roles: ""} + user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd", Roles: ""} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) @@ -933,7 +933,7 @@ func TestFuzzyUserCreate(t *testing.T) { testEmail = utils.FUZZY_STRINGS_EMAILS[i] } - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + testEmail, FullName: testName, Password: "hello"} + user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + testEmail, Nickname: testName, Password: "hello"} _, err := Client.CreateUser(&user, "") if err != nil { @@ -948,7 +948,7 @@ func TestStatuses(t *testing.T) { team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) - user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) diff --git a/api/web_socket_test.go b/api/web_socket_test.go index 4cb49220f9..e52a4f731b 100644 --- a/api/web_socket_test.go +++ b/api/web_socket_test.go @@ -20,7 +20,7 @@ func TestSocket(t *testing.T) { team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) Client.LoginByEmail(team.Domain, user1.Email, "pwd") @@ -39,7 +39,7 @@ func TestSocket(t *testing.T) { t.Fatal(err) } - user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"} + user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) Client.LoginByEmail(team.Domain, user2.Email, "pwd") diff --git a/manualtesting/manual_testing.go b/manualtesting/manual_testing.go index 929f7ab5d8..6c4958a7bb 100644 --- a/manualtesting/manual_testing.go +++ b/manualtesting/manual_testing.go @@ -90,7 +90,7 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) { user := &model.User{ TeamId: teamID, Email: utils.RandomEmail(utils.Range{20, 20}, utils.LOWERCASE), - FullName: username[0], + Nickname: username[0], Password: api.USER_PASSWORD} result, err := client.CreateUser(user, "") diff --git a/model/channel_extra.go b/model/channel_extra.go index a5c9acf714..d1579f9058 100644 --- a/model/channel_extra.go +++ b/model/channel_extra.go @@ -10,7 +10,7 @@ import ( type ExtraMember struct { Id string `json:"id"` - FullName string `json:"full_name"` + Nickname string `json:"nickname"` Email string `json:"email"` Roles string `json:"roles"` Username string `json:"username"` @@ -21,7 +21,7 @@ func (o *ExtraMember) Sanitize(options map[string]bool) { o.Email = "" } if len(options) == 0 || !options["fullname"] { - o.FullName = "" + o.Nickname = "" } } diff --git a/model/user.go b/model/user.go index c5a4d846dc..88f8f718ae 100644 --- a/model/user.go +++ b/model/user.go @@ -37,7 +37,7 @@ type User struct { AuthData string `json:"auth_data"` Email string `json:"email"` EmailVerified bool `json:"email_verified"` - FullName string `json:"full_name"` + Nickname string `json:"nickname"` Roles string `json:"roles"` LastActivityAt int64 `json:"last_activity_at"` LastPingAt int64 `json:"last_ping_at"` @@ -82,8 +82,8 @@ func (u *User) IsValid() *AppError { return NewAppError("User.IsValid", "Invalid email", "user_id="+u.Id) } - if len(u.FullName) > 64 { - return NewAppError("User.IsValid", "Invalid full name", "user_id="+u.Id) + if len(u.Nickname) > 64 { + return NewAppError("User.IsValid", "Invalid nickname", "user_id="+u.Id) } return nil @@ -152,7 +152,7 @@ func (u *User) SetDefaultNotifications() { u.NotifyProps["first_name"] = "false" u.NotifyProps["all"] = "true" u.NotifyProps["channel"] = "true" - splitName := strings.Split(u.FullName, " ") + splitName := strings.Split(u.Nickname, " ") if len(splitName) > 0 && splitName[0] != "" { u.NotifyProps["first_name"] = "true" u.NotifyProps["mention_keys"] += "," + splitName[0] @@ -191,7 +191,7 @@ func (u *User) Sanitize(options map[string]bool) { u.Email = "" } if len(options) != 0 && !options["fullname"] { - u.FullName = "" + u.Nickname = "" } if len(options) != 0 && !options["skypeid"] { // TODO - fill in when SkypeId is added to user model diff --git a/model/user_test.go b/model/user_test.go index df9ac19c22..013bacc1cc 100644 --- a/model/user_test.go +++ b/model/user_test.go @@ -80,12 +80,12 @@ func TestUserIsValid(t *testing.T) { } user.Email = "test@nowhere.com" - user.FullName = strings.Repeat("01234567890", 20) + user.Nickname = strings.Repeat("01234567890", 20) if err := user.IsValid(); err == nil { t.Fatal() } - user.FullName = "" + user.Nickname = "" if err := user.IsValid(); err != nil { t.Fatal(err) } diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go index d61fbcdd06..6820b2326c 100644 --- a/store/sql_channel_store.go +++ b/store/sql_channel_store.go @@ -81,11 +81,11 @@ func (s SqlChannelStore) Save(channel *model.Channel) StoreChannel { if strings.Contains(err.Error(), "Duplicate entry") && strings.Contains(err.Error(), "for key 'Name'") { dupChannel := model.Channel{} s.GetReplica().SelectOne(&dupChannel, "SELECT * FROM Channels WHERE TeamId=? AND Name=? AND DeleteAt > 0", channel.TeamId, channel.Name) - if (dupChannel.DeleteAt > 0) { + if dupChannel.DeleteAt > 0 { result.Err = model.NewAppError("SqlChannelStore.Update", "A channel with that name was previously created", "id="+channel.Id+", "+err.Error()) } else { result.Err = model.NewAppError("SqlChannelStore.Update", "A channel with that name already exists", "id="+channel.Id+", "+err.Error()) - } + } } else { result.Err = model.NewAppError("SqlChannelStore.Save", "We couldn't save the channel", "id="+channel.Id+", "+err.Error()) } @@ -119,7 +119,7 @@ func (s SqlChannelStore) Update(channel *model.Channel) StoreChannel { if strings.Contains(err.Error(), "Duplicate entry") && strings.Contains(err.Error(), "for key 'Name'") { dupChannel := model.Channel{} s.GetReplica().SelectOne(&dupChannel, "SELECT * FROM Channels WHERE TeamId=? AND Name=? AND DeleteAt > 0", channel.TeamId, channel.Name) - if (dupChannel.DeleteAt > 0) { + if dupChannel.DeleteAt > 0 { result.Err = model.NewAppError("SqlChannelStore.Update", "A channel with that name was previously created", "id="+channel.Id+", "+err.Error()) } else { result.Err = model.NewAppError("SqlChannelStore.Update", "A channel with that name already exists", "id="+channel.Id+", "+err.Error()) @@ -358,7 +358,7 @@ func (s SqlChannelStore) GetExtraMembers(channelId string, limit int) StoreChann result := StoreResult{} var members []model.ExtraMember - _, err := s.GetReplica().Select(&members, "SELECT Id, FullName, Email, ChannelMembers.Roles, Username FROM ChannelMembers, Users WHERE ChannelMembers.UserId = Users.Id AND ChannelId = ? LIMIT ?", channelId, limit) + _, err := s.GetReplica().Select(&members, "SELECT Id, Nickname, Email, ChannelMembers.Roles, Username FROM ChannelMembers, Users WHERE ChannelMembers.UserId = Users.Id AND ChannelId = ? LIMIT ?", channelId, limit) if err != nil { result.Err = model.NewAppError("SqlChannelStore.GetExtraMembers", "We couldn't get the extra info for channel members", "channel_id="+channelId+", "+err.Error()) } else { diff --git a/store/sql_channel_store_test.go b/store/sql_channel_store_test.go index 9821e9ad08..9cc1c2b06e 100644 --- a/store/sql_channel_store_test.go +++ b/store/sql_channel_store_test.go @@ -200,13 +200,13 @@ func TestChannelMemberStore(t *testing.T) { u1 := model.User{} u1.TeamId = model.NewId() u1.Email = model.NewId() - u1.FullName = model.NewId() + u1.Nickname = model.NewId() Must(store.User().Save(&u1)) u2 := model.User{} u2.TeamId = model.NewId() u2.Email = model.NewId() - u2.FullName = model.NewId() + u2.Nickname = model.NewId() Must(store.User().Save(&u2)) o1 := model.ChannelMember{} diff --git a/store/sql_store.go b/store/sql_store.go index 7a2d059b91..2e4981e6b3 100644 --- a/store/sql_store.go +++ b/store/sql_store.go @@ -126,9 +126,9 @@ func setupConnection(con_type string, driver string, dataSource string, maxIdle return dbmap } -func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string, afterName string, colType string, defaultValue string) bool { +func (ss SqlStore) DoesColumnExist(tableName string, columnName string) bool { count, err := ss.GetMaster().SelectInt( - `SELECT + `SELECT COUNT(0) AS column_exists FROM information_schema.COLUMNS @@ -145,11 +145,15 @@ func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string, panic("Failed to check if column exists " + err.Error()) } - if count > 0 { + return count > 0 +} + +func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string, afterName string, colType string, defaultValue string) bool { + if ss.DoesColumnExist(tableName, columnName) { return false } - _, err = ss.GetMaster().Exec("ALTER TABLE " + tableName + " ADD " + columnName + " " + colType + " DEFAULT '" + defaultValue + "'" + " AFTER " + afterName) + _, err := ss.GetMaster().Exec("ALTER TABLE " + tableName + " ADD " + columnName + " " + colType + " DEFAULT '" + defaultValue + "'" + " AFTER " + afterName) if err != nil { l4g.Critical("Failed to create column %v", err) time.Sleep(time.Second) @@ -160,29 +164,11 @@ func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string, } func (ss SqlStore) RemoveColumnIfExists(tableName string, columnName string) bool { - count, err := ss.GetMaster().SelectInt( - `SELECT - COUNT(0) AS column_exists - FROM - information_schema.COLUMNS - WHERE - TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = ? - AND COLUMN_NAME = ?`, - tableName, - columnName, - ) - if err != nil { - l4g.Critical("Failed to check if column exists %v", err) - time.Sleep(time.Second) - panic("Failed to check if column exists " + err.Error()) - } - - if count == 0 { + if !ss.DoesColumnExist(tableName, columnName) { return false } - _, err = ss.GetMaster().Exec("ALTER TABLE " + tableName + " DROP COLUMN " + columnName) + _, err := ss.GetMaster().Exec("ALTER TABLE " + tableName + " DROP COLUMN " + columnName) if err != nil { l4g.Critical("Failed to drop column %v", err) time.Sleep(time.Second) @@ -192,6 +178,25 @@ func (ss SqlStore) RemoveColumnIfExists(tableName string, columnName string) boo return true } +func (ss SqlStore) RenameColumnIfExists(tableName string, oldColumnName string, newColumnName string, colType string) bool { + if !ss.DoesColumnExist(tableName, oldColumnName) { + return false + } + + _, err := ss.GetMaster().Exec("ALTER TABLE " + tableName + " CHANGE " + oldColumnName + " " + newColumnName + " " + colType) + + // when we eventually support PostgreSQL, we can use the following instead + //_, err := ss.GetMaster().Exec("ALTER TABLE " + tableName + " RENAME COLUMN " + oldColumnName + " TO " + newColumnName) + + if err != nil { + l4g.Critical("Failed to rename column %v", err) + time.Sleep(time.Second) + panic("Failed to drop column " + err.Error()) + } + + return true +} + func (ss SqlStore) CreateIndexIfNotExists(indexName string, tableName string, columnName string) { ss.createIndexIfNotExists(indexName, tableName, columnName, false) } diff --git a/store/sql_user_store.go b/store/sql_user_store.go index 665e4d697d..9e1cc93311 100644 --- a/store/sql_user_store.go +++ b/store/sql_user_store.go @@ -25,7 +25,7 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore { table.ColMap("Password").SetMaxSize(128) table.ColMap("AuthData").SetMaxSize(128) table.ColMap("Email").SetMaxSize(128) - table.ColMap("FullName").SetMaxSize(64) + table.ColMap("Nickname").SetMaxSize(64) table.ColMap("Roles").SetMaxSize(64) table.ColMap("Props").SetMaxSize(4000) table.ColMap("NotifyProps").SetMaxSize(2000) @@ -36,10 +36,15 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore { return us } -func (s SqlUserStore) UpgradeSchemaIfNeeded() { - s.CreateColumnIfNotExists("Users","LastPictureUpdate", "LastPasswordUpdate", "bigint(20)", "0") +func (us SqlUserStore) UpgradeSchemaIfNeeded() { + us.CreateColumnIfNotExists("Users", "LastPictureUpdate", "LastPasswordUpdate", "bigint(20)", "0") + + // migrating the FullName column to Nickname for MM-825 + us.RenameColumnIfExists("Users", "FullName", "Nickname", "varchar(64)") } +//func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string, afterName string, colType string, defaultValue string) bool { + func (us SqlUserStore) CreateIndexesIfNotExists() { us.CreateIndexIfNotExists("idx_team_id", "Users", "TeamId") } diff --git a/web/web.go b/web/web.go index e771157d8b..caa779ec91 100644 --- a/web/web.go +++ b/web/web.go @@ -303,8 +303,8 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) { //api.Handle404(w, r) //Bad channel urls just redirect to the town-square for now - - http.Redirect(w,r,"/channels/town-square", http.StatusFound) + + http.Redirect(w, r, "/channels/town-square", http.StatusFound) return } } @@ -351,7 +351,7 @@ func verifyEmail(c *api.Context, w http.ResponseWriter, r *http.Request) { return } else { user := result.Data.(*model.User) - api.FireAndForgetVerifyEmail(user.Id, strings.Split(user.FullName, " ")[0], user.Email, domain, c.TeamUrl) + api.FireAndForgetVerifyEmail(user.Id, strings.Split(user.Nickname, " ")[0], user.Email, domain, c.TeamUrl) http.Redirect(w, r, "/", http.StatusFound) return } From 1c7f0be268046d2b509e23268eebcbbef78c5a24 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Thu, 9 Jul 2015 15:49:23 -0400 Subject: [PATCH 71/90] Renamed all clientside references from full_name to nickname --- web/react/components/channel_header.jsx | 2 +- web/react/components/member_list_team.jsx | 4 ++-- web/react/components/mention_list.jsx | 10 +++++----- web/react/components/post_list.jsx | 2 +- web/react/components/sidebar.jsx | 4 ++-- web/react/components/user_settings.jsx | 14 +++++++------- web/react/stores/user_store.jsx | 4 ++-- web/templates/head.html | 2 +- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx index 2e430489f3..30435dc08c 100644 --- a/web/react/components/channel_header.jsx +++ b/web/react/components/channel_header.jsx @@ -153,7 +153,7 @@ module.exports = React.createClass({ if (isDirect) { if (this.state.users.length > 1) { var contact = this.state.users[((this.state.users[0].id === currentId) ? 1 : 0)]; - channelTitle = ; + channelTitle = ; } } diff --git a/web/react/components/member_list_team.jsx b/web/react/components/member_list_team.jsx index 89b5e49d50..8d69a36d39 100644 --- a/web/react/components/member_list_team.jsx +++ b/web/react/components/member_list_team.jsx @@ -85,8 +85,8 @@ var MemberListTeamItem = React.createClass({ return (
                  - {user.full_name.trim() ? user.full_name : user.username} - {user.full_name.trim() ? user.username : email} + {user.nickname.trim() ? user.nickname : user.username} + {user.nickname.trim() ? user.username : email}
                  {currentRoles} diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index c5ff823461..c04e28849a 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -165,14 +165,14 @@ module.exports = React.createClass({ var all = {}; all.username = "all"; - all.full_name = ""; + all.nickname = ""; all.secondary_text = "Notifies everyone in the team"; all.id = "allmention"; users.push(all); var channel = {}; channel.username = "channel"; - channel.full_name = ""; + channel.nickname = ""; channel.secondary_text = "Notifies everyone in the channel"; channel.id = "channelmention"; users.push(channel); @@ -189,11 +189,11 @@ module.exports = React.createClass({ if (this.alreadyMentioned(users[i].username)) continue; var firstName = "", lastName = ""; - if (users[i].full_name.length > 0) { - var splitName = users[i].full_name.split(' '); + if (users[i].nickname.length > 0) { + var splitName = users[i].nickname.split(' '); firstName = splitName[0].toLowerCase(); lastName = splitName.length > 1 ? splitName[splitName.length-1].toLowerCase() : ""; - users[i].secondary_text = users[i].full_name; + users[i].secondary_text = users[i].nickname; } if (firstName.lastIndexOf(mentionText,0) === 0 diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index 573799a190..5439ca43d2 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -305,7 +305,7 @@ module.exports = React.createClass({ var teammate = utils.getDirectTeammate(channel.id) if (teammate) { - var teammate_name = teammate.full_name.length > 0 ? teammate.full_name : teammate.username; + var teammate_name = teammate.nickname.length > 0 ? teammate.nickname : teammate.username; more_messages = (
                  diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index cae9425d36..7427200487 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -131,7 +131,7 @@ function getStateFromStores() { var channel = ChannelStore.getByName(channelName); if (channel != null) { - channel.display_name = teammate.full_name.trim() != "" ? teammate.full_name : teammate.username; + channel.display_name = teammate.nickname.trim() != "" ? teammate.nickname : teammate.username; channel.teammate_username = teammate.username; channel.status = UserStore.getStatus(teammate.id); @@ -150,7 +150,7 @@ function getStateFromStores() { var tempChannel = {}; tempChannel.fake = true; tempChannel.name = channelName; - tempChannel.display_name = teammate.full_name.trim() != "" ? teammate.full_name : teammate.username; + tempChannel.display_name = teammate.nickname.trim() != "" ? teammate.nickname : teammate.username; tempChannel.status = UserStore.getStatus(teammate.id); tempChannel.last_post_at = 0; readDirectChannels.push(tempChannel); diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx index 38e4b1aeaf..9ec7f64c91 100644 --- a/web/react/components/user_settings.jsx +++ b/web/react/components/user_settings.jsx @@ -316,8 +316,8 @@ var NotificationsTab = React.createClass({ if (this.props.activeSection === 'keys') { var user = this.props.user; var first_name = ""; - if (user.full_name.length > 0) { - first_name = user.full_name.split(' ')[0]; + if (user.nickname.length > 0) { + first_name = user.nickname.split(' ')[0]; } var inputs = []; @@ -399,7 +399,7 @@ var NotificationsTab = React.createClass({ if (this.state.first_name_key) { var first_name = ""; var user = this.props.user; - if (user.full_name.length > 0) first_name = user.full_name.split(' ')[0]; + if (user.nickname.length > 0) first_name = user.nickname.split(' ')[0]; if (first_name != "") keys.push(first_name); } if (this.state.username_key) keys.push(this.props.user.username); @@ -761,12 +761,12 @@ var GeneralTab = React.createClass({ var fullName = firstName + ' ' + lastName; - if (user.full_name === fullName) { + if (user.nickname === fullName) { this.setState({client_error: "You must submit a new name"}) return; } - user.full_name = fullName; + user.nickname = fullName; this.submitUser(user); }, @@ -861,7 +861,7 @@ var GeneralTab = React.createClass({ getInitialState: function() { var user = this.props.user; - var splitStr = user.full_name.split(' '); + var splitStr = user.nickname.split(' '); var firstName = splitStr.shift(); var lastName = splitStr.join(' '); @@ -913,7 +913,7 @@ var GeneralTab = React.createClass({ nameSection = ( ); diff --git a/web/react/stores/user_store.jsx b/web/react/stores/user_store.jsx index dd207ca80b..c293469e56 100644 --- a/web/react/stores/user_store.jsx +++ b/web/react/stores/user_store.jsx @@ -180,8 +180,8 @@ var UserStore = assign({}, EventEmitter.prototype, { if (user && user.notify_props && user.notify_props.mention_keys) { var keys = user.notify_props.mention_keys.split(','); - if (user.full_name.length > 0 && user.notify_props.first_name === "true") { - var first = user.full_name.split(' ')[0]; + if (user.nickname.length > 0 && user.notify_props.first_name === "true") { + var first = user.nickname.split(' ')[0]; if (first.length > 0) keys.push(first); } diff --git a/web/templates/head.html b/web/templates/head.html index 5b423d487f..0cbda28c50 100644 --- a/web/templates/head.html +++ b/web/templates/head.html @@ -59,7 +59,7 @@ var user = window.UserStore.getCurrentUser(true); if (user) { analytics.identify(user.id, { - name: user.full_name, + name: user.nickname, email: user.email, createdAt: user.create_at, username: user.username, From 9d8073328a5e971aaff6285e33e212c08b31b510 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Thu, 9 Jul 2015 17:06:04 -0400 Subject: [PATCH 72/90] Added FirstName and LastName fields to User object and attempt to populate them from the FullName/Nickname field --- model/user.go | 10 ++++++++++ store/sql_user_store.go | 16 ++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/model/user.go b/model/user.go index 88f8f718ae..7265381fdb 100644 --- a/model/user.go +++ b/model/user.go @@ -38,6 +38,8 @@ type User struct { Email string `json:"email"` EmailVerified bool `json:"email_verified"` Nickname string `json:"nickname"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` Roles string `json:"roles"` LastActivityAt int64 `json:"last_activity_at"` LastPingAt int64 `json:"last_ping_at"` @@ -86,6 +88,14 @@ func (u *User) IsValid() *AppError { return NewAppError("User.IsValid", "Invalid nickname", "user_id="+u.Id) } + if len(u.FirstName) > 64 { + return NewAppError("User.IsValid", "Invalid first name", "user_id="+u.Id) + } + + if len(u.LastName) > 64 { + return NewAppError("User.IsValid", "Invalid last name", "user_id="+u.Id) + } + return nil } diff --git a/store/sql_user_store.go b/store/sql_user_store.go index 9e1cc93311..728c6b2437 100644 --- a/store/sql_user_store.go +++ b/store/sql_user_store.go @@ -26,6 +26,8 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore { table.ColMap("AuthData").SetMaxSize(128) table.ColMap("Email").SetMaxSize(128) table.ColMap("Nickname").SetMaxSize(64) + table.ColMap("FirstName").SetMaxSize(64) + table.ColMap("LastName").SetMaxSize(64) table.ColMap("Roles").SetMaxSize(64) table.ColMap("Props").SetMaxSize(4000) table.ColMap("NotifyProps").SetMaxSize(2000) @@ -39,8 +41,18 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore { func (us SqlUserStore) UpgradeSchemaIfNeeded() { us.CreateColumnIfNotExists("Users", "LastPictureUpdate", "LastPasswordUpdate", "bigint(20)", "0") - // migrating the FullName column to Nickname for MM-825 - us.RenameColumnIfExists("Users", "FullName", "Nickname", "varchar(64)") + // migrating the FullName column to Nickname and adding the FirstName and LastName columns for MM-825 + if us.RenameColumnIfExists("Users", "FullName", "Nickname", "varchar(64)") { + us.CreateColumnIfNotExists("Users", "FirstName", "Nickname", "varchar(64)", "") + us.CreateColumnIfNotExists("Users", "LastName", "FirstName", "varchar(64)", "") + + // infer values of first and last name by splitting the previous full name + if _, err := us.GetMaster().Exec("UPDATE Users SET " + + "FirstName = SUBSTRING_INDEX(SUBSTRING_INDEX(Nickname, ' ', 1), ' ', -1), " + + "LastName = SUBSTRING(Nickname, INSTR(Nickname, ' ') + 1)"); err != nil { + panic("Failed to set first and last name columns from nickname " + err.Error()) + } + } } //func (ss SqlStore) CreateColumnIfNotExists(tableName string, columnName string, afterName string, colType string, defaultValue string) bool { From 8dbf03bd10a440db3896c046de7332806f96e889 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Thu, 9 Jul 2015 17:20:18 -0400 Subject: [PATCH 73/90] Add interface to edit a user's nickname directly --- web/react/components/user_settings.jsx | 55 +++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx index 9ec7f64c91..c1ea367f2a 100644 --- a/web/react/components/user_settings.jsx +++ b/web/react/components/user_settings.jsx @@ -752,6 +752,21 @@ var GeneralTab = React.createClass({ this.submitUser(user); }, + submitNickname: function(e) { + e.preventDefault(); + + var user = UserStore.getCurrentUser(); + var nickname = this.state.nickname.trim(); + + if (user.nickname === nickname) { + this.setState({client_error: "You must submit a new nickname"}) + return; + } + + user.nickname = nickname; + + this.submitUser(user); + }, submitName: function(e) { e.preventDefault(); @@ -839,6 +854,9 @@ var GeneralTab = React.createClass({ updateLastName: function(e) { this.setState({ last_name: e.target.value}); }, + updateNickname: function(e) { + this.setState({nickname: e.target.value}); + }, updateEmail: function(e) { this.setState({ email: e.target.value}); }, @@ -865,7 +883,7 @@ var GeneralTab = React.createClass({ var firstName = splitStr.shift(); var lastName = splitStr.join(' '); - return { username: user.username, first_name: firstName, last_name: lastName, + return { username: user.username, first_name: firstName, last_name: lastName, nickname: user.nickname, email: user.email, picture: null }; }, render: function() { @@ -919,6 +937,39 @@ var GeneralTab = React.createClass({ ); } + var nicknameSection; + if (this.props.activeSection === 'nickname') { + var inputs = []; + + inputs.push( +
                  + +
                  + +
                  +
                  + ); + + nicknameSection = ( + + ); + } else { + nicknameSection = ( + + ); + } + var usernameSection; if (this.props.activeSection === 'username') { var inputs = []; @@ -1026,6 +1077,8 @@ var GeneralTab = React.createClass({
                  {usernameSection}
                  + {nicknameSection} +
                  {emailSection}
                  {pictureSection} From b620960cdefe87d9e96958ab526c294ee6502a90 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Fri, 10 Jul 2015 15:51:45 -0400 Subject: [PATCH 74/90] Fix migration code so that LastName is populated correctly for users who only have one word in their FullName --- store/sql_user_store.go | 12 ++++++---- web/react/components/user_settings.jsx | 31 +++++++++++++++----------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/store/sql_user_store.go b/store/sql_user_store.go index 728c6b2437..d8ab4482ed 100644 --- a/store/sql_user_store.go +++ b/store/sql_user_store.go @@ -47,10 +47,14 @@ func (us SqlUserStore) UpgradeSchemaIfNeeded() { us.CreateColumnIfNotExists("Users", "LastName", "FirstName", "varchar(64)", "") // infer values of first and last name by splitting the previous full name - if _, err := us.GetMaster().Exec("UPDATE Users SET " + - "FirstName = SUBSTRING_INDEX(SUBSTRING_INDEX(Nickname, ' ', 1), ' ', -1), " + - "LastName = SUBSTRING(Nickname, INSTR(Nickname, ' ') + 1)"); err != nil { - panic("Failed to set first and last name columns from nickname " + err.Error()) + if _, err := us.GetMaster().Exec("UPDATE Users SET FirstName = SUBSTRING_INDEX(SUBSTRING_INDEX(Nickname, ' ', 1), ' ', -1)"); err != nil { + panic("Failed to set first name from nickname " + err.Error()) + } + + // only set the last name from full names that are comprised of multiple words (ie that have at least one space in them) + if _, err := us.GetMaster().Exec("Update Users SET LastName = SUBSTRING(Nickname, INSTR(Nickname, ' ') + 1) " + + "WHERE CHAR_LENGTH(REPLACE(Nickname, ' ', '')) < CHAR_LENGTH(Nickname)"); err != nil { + panic("Failed to set last name from nickname " + err.Error()) } } } diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx index c1ea367f2a..e6eb2b4513 100644 --- a/web/react/components/user_settings.jsx +++ b/web/react/components/user_settings.jsx @@ -774,14 +774,13 @@ var GeneralTab = React.createClass({ var firstName = this.state.first_name.trim(); var lastName = this.state.last_name.trim(); - var fullName = firstName + ' ' + lastName; - - if (user.nickname === fullName) { - this.setState({client_error: "You must submit a new name"}) + if (user.first_name === firstName && user.last_name === lastName) { + this.setState({client_error: "You must submit a new first or last name"}) return; } - user.nickname = fullName; + user.first_name = firstName; + user.last_name = lastName; this.submitUser(user); }, @@ -879,11 +878,7 @@ var GeneralTab = React.createClass({ getInitialState: function() { var user = this.props.user; - var splitStr = user.nickname.split(' '); - var firstName = splitStr.shift(); - var lastName = splitStr.join(' '); - - return { username: user.username, first_name: firstName, last_name: lastName, nickname: user.nickname, + return { username: user.username, first_name: user.first_name, last_name: user.last_name, nickname: user.nickname, email: user.email, picture: null }; }, render: function() { @@ -919,7 +914,7 @@ var GeneralTab = React.createClass({ nameSection = ( ); } else { + var full_name = ""; + + if (user.first_name && user.last_name) { + full_name = user.first_name + " " + user.last_name; + } else if (user.first_name) { + full_name = user.first_name; + } else if (user.last_name) { + full_name = user.last_name; + } + nameSection = ( ); From 31415c8ddcc915c64c791ece2e2439883b7cf6ae Mon Sep 17 00:00:00 2001 From: hmhealey Date: Fri, 10 Jul 2015 16:52:32 -0400 Subject: [PATCH 75/90] Sanitize FirstName and LastName fields of an object instead of the nickname when ShowFullName is set to false --- model/channel_extra.go | 3 --- model/user.go | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/model/channel_extra.go b/model/channel_extra.go index d1579f9058..3a918b5244 100644 --- a/model/channel_extra.go +++ b/model/channel_extra.go @@ -20,9 +20,6 @@ func (o *ExtraMember) Sanitize(options map[string]bool) { if len(options) == 0 || !options["email"] { o.Email = "" } - if len(options) == 0 || !options["fullname"] { - o.Nickname = "" - } } type ChannelExtra struct { diff --git a/model/user.go b/model/user.go index 7265381fdb..5422f68a58 100644 --- a/model/user.go +++ b/model/user.go @@ -201,7 +201,8 @@ func (u *User) Sanitize(options map[string]bool) { u.Email = "" } if len(options) != 0 && !options["fullname"] { - u.Nickname = "" + u.FirstName = "" + u.LastName = "" } if len(options) != 0 && !options["skypeid"] { // TODO - fill in when SkypeId is added to user model From fb42a74613a6a5c4ebe316cf6e528d74aa74bb78 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Mon, 13 Jul 2015 10:22:10 -0400 Subject: [PATCH 76/90] Use User.FirstName instead of trying to infer a user's first name from their nickname in multiple places. --- api/post.go | 9 ++------- api/user.go | 4 ++-- web/web.go | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/api/post.go b/api/post.go index ab89133d56..e625912734 100644 --- a/api/post.go +++ b/api/post.go @@ -301,10 +301,7 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) { // If turned on, add the user's case sensitive first name if profile.NotifyProps["first_name"] == "true" { - splitName := strings.Split(profile.Nickname, " ") - if len(splitName) > 0 && splitName[0] != "" { - keywordMap[splitName[0]] = append(keywordMap[splitName[0]], profile.Id) - } + keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id) } } @@ -395,10 +392,8 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) { continue } - firstName := strings.Split(profileMap[id].Nickname, " ")[0] - bodyPage := NewServerTemplatePage("post_body", teamUrl) - bodyPage.Props["Nickname"] = firstName + bodyPage.Props["Nickname"] = profileMap[id].FirstName bodyPage.Props["TeamName"] = teamName bodyPage.Props["ChannelName"] = channelName bodyPage.Props["BodyText"] = bodyText diff --git a/api/user.go b/api/user.go index 48a9f0da2d..10948c63d8 100644 --- a/api/user.go +++ b/api/user.go @@ -181,14 +181,14 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User { l4g.Error("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", ruser.Id, ruser.TeamId, err) } - //fireAndForgetWelcomeEmail(strings.Split(ruser.Nickname, " ")[0], ruser.Email, team.Name, c.TeamUrl+"/channels/town-square") + //fireAndForgetWelcomeEmail(ruser.FirstName, ruser.Email, team.Name, c.TeamUrl+"/channels/town-square") if user.EmailVerified { if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { l4g.Error("Failed to set email verified err=%v", cresult.Err) } } else { - FireAndForgetVerifyEmail(result.Data.(*model.User).Id, strings.Split(ruser.Nickname, " ")[0], ruser.Email, team.Name, c.TeamUrl) + FireAndForgetVerifyEmail(result.Data.(*model.User).Id, ruser.FirstName, ruser.Email, team.Name, c.TeamUrl) } ruser.Sanitize(map[string]bool{}) diff --git a/web/web.go b/web/web.go index caa779ec91..b11e6e6b11 100644 --- a/web/web.go +++ b/web/web.go @@ -351,7 +351,7 @@ func verifyEmail(c *api.Context, w http.ResponseWriter, r *http.Request) { return } else { user := result.Data.(*model.User) - api.FireAndForgetVerifyEmail(user.Id, strings.Split(user.Nickname, " ")[0], user.Email, domain, c.TeamUrl) + api.FireAndForgetVerifyEmail(user.Id, user.FirstName, user.Email, domain, c.TeamUrl) http.Redirect(w, r, "/", http.StatusFound) return } From 079538d9e7d22cd8d5f782f4f7434858bd80d914 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Mon, 13 Jul 2015 15:39:53 -0400 Subject: [PATCH 77/90] Use User.FirstName for notifications instead of trying to determine it from User.Nickname --- web/react/components/mention_list.jsx | 14 +++----------- web/react/components/user_settings.jsx | 23 +++++++---------------- web/react/stores/user_store.jsx | 6 +----- 3 files changed, 11 insertions(+), 32 deletions(-) diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index c04e28849a..524f1b3378 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -188,21 +188,13 @@ module.exports = React.createClass({ for (var i = 0; i < users.length && index < MAX_ITEMS_IN_LIST; i++) { if (this.alreadyMentioned(users[i].username)) continue; - var firstName = "", lastName = ""; - if (users[i].nickname.length > 0) { - var splitName = users[i].nickname.split(' '); - firstName = splitName[0].toLowerCase(); - lastName = splitName.length > 1 ? splitName[splitName.length-1].toLowerCase() : ""; - users[i].secondary_text = users[i].nickname; - } - - if (firstName.lastIndexOf(mentionText,0) === 0 - || lastName.lastIndexOf(mentionText,0) === 0 || users[i].username.lastIndexOf(mentionText,0) === 0) { + if (users[i].first_name.lastIndexOf(mentionText,0) === 0 + || users[i].last_name.lastIndexOf(mentionText,0) === 0 || users[i].username.lastIndexOf(mentionText,0) === 0) { mentions[index] = ( 0) { - first_name = user.nickname.split(' ')[0]; - } - var inputs = []; - if (first_name != "") { + if (user.first_name) { inputs.push(
                  @@ -396,14 +392,9 @@ var NotificationsTab = React.createClass({ ); } else { var keys = []; - if (this.state.first_name_key) { - var first_name = ""; - var user = this.props.user; - if (user.nickname.length > 0) first_name = user.nickname.split(' ')[0]; - if (first_name != "") keys.push(first_name); - } - if (this.state.username_key) keys.push(this.props.user.username); - if (this.state.mention_key) keys.push('@'+this.props.user.username); + if (this.state.first_name_key) keys.push(user.first_name); + if (this.state.username_key) keys.push(user.username); + if (this.state.mention_key) keys.push('@'+user.username); if (this.state.all_key) keys.push('@all'); if (this.state.channel_key) keys.push('@channel'); if (this.state.custom_keys.length > 0) keys = keys.concat(this.state.custom_keys.split(',')); diff --git a/web/react/stores/user_store.jsx b/web/react/stores/user_store.jsx index c293469e56..e417e72ff3 100644 --- a/web/react/stores/user_store.jsx +++ b/web/react/stores/user_store.jsx @@ -180,11 +180,7 @@ var UserStore = assign({}, EventEmitter.prototype, { if (user && user.notify_props && user.notify_props.mention_keys) { var keys = user.notify_props.mention_keys.split(','); - if (user.nickname.length > 0 && user.notify_props.first_name === "true") { - var first = user.nickname.split(' ')[0]; - if (first.length > 0) keys.push(first); - } - + if (user.first_name && user.notify_props.first_name === "true") keys.push(user.first_name); if (user.notify_props.all === "true") keys.push('@all'); if (user.notify_props.channel === "true") keys.push('@channel'); From e737ec3eb5d16a8ea371c0bd9379d8d0a937764c Mon Sep 17 00:00:00 2001 From: hmhealey Date: Mon, 13 Jul 2015 15:43:05 -0400 Subject: [PATCH 78/90] Allow Users to be notified using @all, @channel, and their first name when other notifications are disabled --- api/post.go | 8 ++++---- web/react/stores/user_store.jsx | 16 +++++++--------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/api/post.go b/api/post.go index e625912734..5b7983386c 100644 --- a/api/post.go +++ b/api/post.go @@ -298,11 +298,11 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) { for _, k := range splitKeys { keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id) } + } - // If turned on, add the user's case sensitive first name - if profile.NotifyProps["first_name"] == "true" { - keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id) - } + // If turned on, add the user's case sensitive first name + if profile.NotifyProps["first_name"] == "true" { + keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id) } // Add @all to keywords if user has them turned on diff --git a/web/react/stores/user_store.jsx b/web/react/stores/user_store.jsx index e417e72ff3..b0ea719d48 100644 --- a/web/react/stores/user_store.jsx +++ b/web/react/stores/user_store.jsx @@ -177,17 +177,15 @@ var UserStore = assign({}, EventEmitter.prototype, { }, getCurrentMentionKeys: function() { var user = this.getCurrentUser(); - if (user && user.notify_props && user.notify_props.mention_keys) { - var keys = user.notify_props.mention_keys.split(','); - if (user.first_name && user.notify_props.first_name === "true") keys.push(user.first_name); - if (user.notify_props.all === "true") keys.push('@all'); - if (user.notify_props.channel === "true") keys.push('@channel'); + var keys = []; - return keys; - } else { - return []; - } + if (user.notify_props && user.notify_props.mention_keys) keys = keys.concat(user.notify_props.mention_keys.split(',')); + if (user.first_name && user.notify_props.first_name === "true") keys.push(user.first_name); + if (user.notify_props.all === "true") keys.push('@all'); + if (user.notify_props.channel === "true") keys.push('@channel'); + + return keys; }, getLastVersion: function() { return BrowserStore.getItem("last_version", ''); From abcb44089c421872de582584049ca4cc6b268f37 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Mon, 13 Jul 2015 16:43:59 -0400 Subject: [PATCH 79/90] Add tests to check if a User's first/last name are valid --- model/user_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/model/user_test.go b/model/user_test.go index 013bacc1cc..4db099f97f 100644 --- a/model/user_test.go +++ b/model/user_test.go @@ -89,4 +89,21 @@ func TestUserIsValid(t *testing.T) { if err := user.IsValid(); err != nil { t.Fatal(err) } + + user.FirstName = "" + user.LastName = "" + if err := user.IsValid(); err != nil { + t.Fatal(err) + } + + user.FirstName = strings.Repeat("01234567890", 20) + if err := user.IsValid(); err == nil { + t.Fatal(err) + } + + user.FirstName = "" + user.LastName = strings.Repeat("01234567890", 20) + if err := user.IsValid(); err == nil { + t.Fatal(err) + } } From 098cbcdc21effeebe7e57fbd912a785e85cbfc5d Mon Sep 17 00:00:00 2001 From: hmhealey Date: Mon, 13 Jul 2015 17:47:57 -0400 Subject: [PATCH 80/90] Unify all locations where we determine a user's display named based off of their nickname/username into a helper function --- api/team.go | 7 +------ model/user.go | 8 ++++++++ model/user_test.go | 13 +++++++++++++ web/react/components/member_list_team.jsx | 4 ++-- web/react/components/sidebar.jsx | 4 ++-- web/react/utils/utils.jsx | 10 ++++++++-- 6 files changed, 34 insertions(+), 12 deletions(-) diff --git a/api/team.go b/api/team.go index dc7804b4d4..7a4cabf7e1 100644 --- a/api/team.go +++ b/api/team.go @@ -487,12 +487,7 @@ func InviteMembers(team *model.Team, user *model.User, invites []string) { teamUrl = fmt.Sprintf("http://%v.%v", team.Domain, utils.Cfg.ServiceSettings.Domain) } - sender := "" - if len(strings.TrimSpace(user.Nickname)) == 0 { - sender = user.Username - } else { - sender = user.Nickname - } + sender := user.GetDisplayName() senderRole := "" if strings.Contains(user.Roles, model.ROLE_ADMIN) || strings.Contains(user.Roles, model.ROLE_SYSTEM_ADMIN) { diff --git a/model/user.go b/model/user.go index 5422f68a58..5b603cd92a 100644 --- a/model/user.go +++ b/model/user.go @@ -237,6 +237,14 @@ func (u *User) AddNotifyProp(key string, value string) { u.NotifyProps[key] = value } +func (u *User) GetDisplayName() string { + if u.Nickname != "" { + return u.Nickname + } else { + return u.Username + } +} + // UserFromJson will decode the input and return a User func UserFromJson(data io.Reader) *User { decoder := json.NewDecoder(data) diff --git a/model/user_test.go b/model/user_test.go index 4db099f97f..a0c62676ae 100644 --- a/model/user_test.go +++ b/model/user_test.go @@ -107,3 +107,16 @@ func TestUserIsValid(t *testing.T) { t.Fatal(err) } } + +func TestUserGetDisplayName(t *testing.T) { + user := User{FirstName: "first", LastName: "last", Username: "user"} + + if displayName := user.GetDisplayName(); displayName != "user" { + t.Fatal("Display name should be username") + } + + user.Nickname = "nickname" + if displayName := user.GetDisplayName(); displayName != "nickname" { + t.Fatal("Display name should be nickname") + } +} diff --git a/web/react/components/member_list_team.jsx b/web/react/components/member_list_team.jsx index 8d69a36d39..6f1d83193a 100644 --- a/web/react/components/member_list_team.jsx +++ b/web/react/components/member_list_team.jsx @@ -85,8 +85,8 @@ var MemberListTeamItem = React.createClass({ return (
                  - {user.nickname.trim() ? user.nickname : user.username} - {user.nickname.trim() ? user.username : email} + {utils.getDisplayName(user)} + {email}
                  {currentRoles} diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index 7427200487..65727c5972 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -131,7 +131,7 @@ function getStateFromStores() { var channel = ChannelStore.getByName(channelName); if (channel != null) { - channel.display_name = teammate.nickname.trim() != "" ? teammate.nickname : teammate.username; + channel.display_name = utils.getDisplayName(teammate); channel.teammate_username = teammate.username; channel.status = UserStore.getStatus(teammate.id); @@ -150,7 +150,7 @@ function getStateFromStores() { var tempChannel = {}; tempChannel.fake = true; tempChannel.name = channelName; - tempChannel.display_name = teammate.nickname.trim() != "" ? teammate.nickname : teammate.username; + tempChannel.display_name = utils.getDisplayName(teammate); tempChannel.status = UserStore.getStatus(teammate.id); tempChannel.last_post_at = 0; readDirectChannels.push(tempChannel); diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 7186251e7f..5a1a7ee738 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -796,7 +796,6 @@ module.exports.getHomeLink = function() { return window.location.protocol + "//" + parts.join("."); } - module.exports.changeColor =function(col, amt) { var usePound = false; @@ -824,5 +823,12 @@ module.exports.changeColor =function(col, amt) { else if (g < 0) g = 0; return (usePound?"#":"") + String("000000" + (g | (b << 8) | (r << 16)).toString(16)).slice(-6); - +}; + +module.exports.getDisplayName = function(user) { + if (user.nickname && user.nickname.trim().length > 0) { + return user.nickname; + } else { + return user.username; + } }; From 1dba330146a10718a2fc9eac0ae7d6e1d6bc0d79 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Tue, 14 Jul 2015 17:07:19 -0400 Subject: [PATCH 81/90] Use a user's full name as their display name if a nickname hasn't been specified --- model/user.go | 14 ++++++++++++++ model/user_test.go | 32 +++++++++++++++++++++++++++++++- web/react/utils/utils.jsx | 20 +++++++++++++++++++- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/model/user.go b/model/user.go index 5b603cd92a..727165b8c2 100644 --- a/model/user.go +++ b/model/user.go @@ -237,9 +237,23 @@ func (u *User) AddNotifyProp(key string, value string) { u.NotifyProps[key] = value } +func (u *User) GetFullName() string { + if u.FirstName != "" && u.LastName != "" { + return u.FirstName + " " + u.LastName + } else if u.FirstName != "" { + return u.FirstName + } else if u.LastName != "" { + return u.LastName + } else { + return "" + } +} + func (u *User) GetDisplayName() string { if u.Nickname != "" { return u.Nickname + } else if fullName := u.GetFullName(); fullName != "" { + return fullName } else { return u.Username } diff --git a/model/user_test.go b/model/user_test.go index a0c62676ae..a48c3f2e78 100644 --- a/model/user_test.go +++ b/model/user_test.go @@ -108,13 +108,43 @@ func TestUserIsValid(t *testing.T) { } } +func TestUserGetFullName(t *testing.T) { + user := User{} + + if fullName := user.GetFullName(); fullName != "" { + t.Fatal("Full name should be blank") + } + + user.FirstName = "first" + if fullName := user.GetFullName(); fullName != "first" { + t.Fatal("Full name should be first name") + } + + user.FirstName = "" + user.LastName = "last" + if fullName := user.GetFullName(); fullName != "last" { + t.Fatal("Full name should be last name") + } + + user.FirstName = "first" + if fullName := user.GetFullName(); fullName != "first last" { + t.Fatal("Full name should be first name and last name") + } +} + func TestUserGetDisplayName(t *testing.T) { - user := User{FirstName: "first", LastName: "last", Username: "user"} + user := User{Username: "user"} if displayName := user.GetDisplayName(); displayName != "user" { t.Fatal("Display name should be username") } + user.FirstName = "first" + user.LastName = "last" + if displayName := user.GetDisplayName(); displayName != "first last" { + t.Fatal("Display name should be full name") + } + user.Nickname = "nickname" if displayName := user.GetDisplayName(); displayName != "nickname" { t.Fatal("Display name should be nickname") diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 5a1a7ee738..416ea5ae46 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -825,10 +825,28 @@ module.exports.changeColor =function(col, amt) { return (usePound?"#":"") + String("000000" + (g | (b << 8) | (r << 16)).toString(16)).slice(-6); }; +module.exports.getFullName = function(user) { + if (user.first_name && user.last_name) { + return user.first_name + " " + user.last_name; + } else if (user.first_name) { + return user.first_name; + } else if (user.last_name) { + return user.last_name; + } else { + return ""; + } +}; + module.exports.getDisplayName = function(user) { if (user.nickname && user.nickname.trim().length > 0) { return user.nickname; } else { - return user.username; + var fullName = module.exports.getFullName(user); + + if (fullName) { + return fullName; + } else { + return user.username; + } } }; From 876ad10f6f14579336a89c36b4937311326149d4 Mon Sep 17 00:00:00 2001 From: =Corey Hulen Date: Fri, 17 Jul 2015 15:41:55 -0800 Subject: [PATCH 82/90] Fixes javascript console errors --- web/react/components/mention_list.jsx | 4 ++-- web/templates/head.html | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index 524f1b3378..7e939812d5 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -188,8 +188,8 @@ module.exports = React.createClass({ for (var i = 0; i < users.length && index < MAX_ITEMS_IN_LIST; i++) { if (this.alreadyMentioned(users[i].username)) continue; - if (users[i].first_name.lastIndexOf(mentionText,0) === 0 - || users[i].last_name.lastIndexOf(mentionText,0) === 0 || users[i].username.lastIndexOf(mentionText,0) === 0) { + if ((users[i].first_name && users[i].first_name.lastIndexOf(mentionText,0) === 0) + || (users[i].last_name && users[i].last_name.lastIndexOf(mentionText,0) === 0) || users[i].username.lastIndexOf(mentionText,0) === 0) { mentions[index] = ( + + + - - {{end}} From d0e4d71ac39bfdbe6d714c6693d3d803bfa811a2 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Sat, 18 Jul 2015 16:32:22 -0400 Subject: [PATCH 83/90] Fixed the labels on the mention list for users without their first/last name configured --- web/react/components/mention_list.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx index 7e939812d5..71a6083d23 100644 --- a/web/react/components/mention_list.jsx +++ b/web/react/components/mention_list.jsx @@ -7,6 +7,7 @@ var AppDispatcher = require('../dispatcher/app_dispatcher.jsx'); var Mention = require('./mention.jsx'); var Constants = require('../utils/constants.jsx'); +var Utils = require('../utils/utils.jsx'); var ActionTypes = Constants.ActionTypes; var MAX_HEIGHT_LIST = 292; @@ -194,7 +195,7 @@ module.exports = React.createClass({ Date: Sat, 18 Jul 2015 16:50:44 -0700 Subject: [PATCH 84/90] Removed remenant of non-hover system --- web/react/components/post_right.jsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/web/react/components/post_right.jsx b/web/react/components/post_right.jsx index cc6751738e..f1ced7b25e 100644 --- a/web/react/components/post_right.jsx +++ b/web/react/components/post_right.jsx @@ -240,9 +240,7 @@ CommentPost = React.createClass({
                • { isOwner ?
                  - +
                  • Edit
                  • Delete
                  • From 12c19e3390fa501b25b1b746ddc3f48e525d7fff Mon Sep 17 00:00:00 2001 From: Reed Garmsen Date: Sun, 19 Jul 2015 20:31:37 -0700 Subject: [PATCH 85/90] Added channel name to RHS comment threads; minor css changes to compensate for addition --- web/react/components/post_right.jsx | 6 ++++++ web/react/components/search_results.jsx | 2 +- web/sass-files/sass/partials/_post_right.scss | 7 ++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/web/react/components/post_right.jsx b/web/react/components/post_right.jsx index cc6751738e..9ec9d8eaf8 100644 --- a/web/react/components/post_right.jsx +++ b/web/react/components/post_right.jsx @@ -68,6 +68,7 @@ RootPost = React.createClass({ var filenames = this.props.post.filenames; var isOwner = UserStore.getCurrentId() == this.props.post.user_id; var timestamp = UserStore.getProfile(this.props.post.user_id).update_at; + var channel = ChannelStore.get(this.props.post.channel_id); var type = "Post"; if (this.props.post.root_id.length > 0) { @@ -79,6 +80,10 @@ RootPost = React.createClass({ currentUserCss = "current--user"; } + if (channel) { + channelName = (channel.type === 'D') ? "Private Message" : channel.display_name; + } + if (filenames) { var postFiles = []; var images = []; @@ -118,6 +123,7 @@ RootPost = React.createClass({ return (
                    +
                    { channelName }
                    diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx index 1fd974642f..643ad112b2 100644 --- a/web/react/components/search_results.jsx +++ b/web/react/components/search_results.jsx @@ -77,7 +77,7 @@ var SearchItem = React.createClass({ var message = utils.textToJsx(this.props.post.message, {searchTerm: this.props.term, noMentionHighlight: !this.props.isMentionSearch}); var channelName = ""; - var channel = ChannelStore.get(this.props.post.channel_id) + var channel = ChannelStore.get(this.props.post.channel_id); var timestamp = UserStore.getCurrentUser().update_at; if (channel) { diff --git a/web/sass-files/sass/partials/_post_right.scss b/web/sass-files/sass/partials/_post_right.scss index 8816393c8f..4cf3e32a17 100644 --- a/web/sass-files/sass/partials/_post_right.scss +++ b/web/sass-files/sass/partials/_post_right.scss @@ -11,7 +11,7 @@ .post { &.post--root { - padding: 1em 1em 0; + padding: 0 1em 0; margin: 1em 0; hr { border-color: #DDD; @@ -62,6 +62,11 @@ } } +.post-right-channel__name { + font-weight: 600; + margin: 0 0 10px 0; +} + .post-right-root-container li { display: inline; list-style-type: none; From 5e27bb3e618d94075e4fb198a937e24338bb0612 Mon Sep 17 00:00:00 2001 From: JoramWilander Date: Mon, 20 Jul 2015 08:58:18 -0400 Subject: [PATCH 86/90] fixes mm-831 post timestamps now update --- web/react/components/post.jsx | 8 ++++++-- web/react/components/post_list.jsx | 16 ++++++++++------ web/react/components/post_right.jsx | 11 +++++++++-- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx index 5457e1cd33..e72a2d001c 100644 --- a/web/react/components/post.jsx +++ b/web/react/components/post.jsx @@ -34,6 +34,10 @@ module.exports = React.createClass({ results: null }); }, + forceUpdateInfo: function() { + this.refs.info.forceUpdate(); + this.refs.header.forceUpdate(); + }, getInitialState: function() { return { }; }, @@ -80,9 +84,9 @@ module.exports = React.createClass({
                    : null }
                    - + - +
                • diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index 5439ca43d2..c058455bae 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -49,6 +49,7 @@ module.exports = React.createClass({ PostStore.addChangeListener(this._onChange); ChannelStore.addChangeListener(this._onChange); + UserStore.addStatusesChangeListener(this._onTimeChange); SocketStore.addChangeListener(this._onSocketChange); $(".post-list-holder-by-time").perfectScrollbar(); @@ -128,6 +129,7 @@ module.exports = React.createClass({ componentWillUnmount: function() { PostStore.removeChangeListener(this._onChange); ChannelStore.removeChangeListener(this._onChange); + UserStore.removeStatusesChangeListener(this._onTimeChange); SocketStore.removeChangeListener(this._onSocketChange); $('body').off('click.userpopover'); }, @@ -206,12 +208,8 @@ module.exports = React.createClass({ var index = post_list.order.indexOf(msg.props.post_id); if (index > -1) post_list.order.splice(index, 1); - var scrollSave = $(".post-list-holder-by-time").scrollTop(); - this.setState({ post_list: post_list }); - $(".post-list-holder-by-time").scrollTop(scrollSave) - PostStore.storePosts(msg.channel_id, post_list); } else { AsyncClient.getPosts(true, msg.channel_id); @@ -220,10 +218,16 @@ module.exports = React.createClass({ if (activeRootPostId === msg.props.post_id && UserStore.getCurrentId() != msg.user_id) { $('#post_deleted').modal('show'); } - } else if(msg.action == "new_user") { + } else if (msg.action == "new_user") { AsyncClient.getProfiles(); } }, + _onTimeChange: function() { + for (var id in this.state.post_list.posts) { + if (!this.refs[id]) continue; + this.refs[id].forceUpdateInfo(); + } + }, getMorePosts: function(e) { e.preventDefault(); @@ -415,7 +419,7 @@ module.exports = React.createClass({ // it is the last comment if it is last post in the channel or the next post has a different root post var isLastComment = utils.isComment(post) && (i === 0 || posts[order[i-1]].root_id != post.root_id); - var postCtl = ; + var postCtl = ; currentPostDay = utils.getDateForUnixTicks(post.create_at); if (currentPostDay.toDateString() != previousPostDay.toDateString()) { diff --git a/web/react/components/post_right.jsx b/web/react/components/post_right.jsx index f1ced7b25e..024bff26cc 100644 --- a/web/react/components/post_right.jsx +++ b/web/react/components/post_right.jsx @@ -280,6 +280,7 @@ module.exports = React.createClass({ componentDidMount: function() { PostStore.addSelectedPostChangeListener(this._onChange); PostStore.addChangeListener(this._onChangeAll); + UserStore.addStatusesChangeListener(this._onTimeChange); this.resize(); var self = this; $(window).resize(function(){ @@ -292,6 +293,7 @@ module.exports = React.createClass({ componentWillUnmount: function() { PostStore.removeSelectedPostChangeListener(this._onChange); PostStore.removeChangeListener(this._onChangeAll); + UserStore.removeStatusesChangeListener(this._onTimeChange); }, _onChange: function() { if (this.isMounted()) { @@ -302,7 +304,6 @@ module.exports = React.createClass({ } }, _onChangeAll: function() { - if (this.isMounted()) { // if something was changed in the channel like adding a @@ -331,6 +332,12 @@ module.exports = React.createClass({ this.setState(getStateFromStores()); } }, + _onTimeChange: function() { + for (var id in this.state.post_list.posts) { + if (!this.refs[id]) continue; + this.refs[id].forceUpdate(); + } + }, getInitialState: function() { return getStateFromStores(); }, @@ -390,7 +397,7 @@ module.exports = React.createClass({
                  { posts_array.map(function(cpost) { - return + return })}
                  From 74ff46bb987c0ed8594ace4b8d4bd15b14b1ef7e Mon Sep 17 00:00:00 2001 From: hmhealey Date: Mon, 20 Jul 2015 09:52:25 -0400 Subject: [PATCH 87/90] Changed Makefile to restart existing mysql/redis containers if they've been stopped --- Makefile | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 724497d918..589521c371 100644 --- a/Makefile +++ b/Makefile @@ -41,17 +41,23 @@ build: install: @go get $(GOFLAGS) github.com/tools/godep - @if [ $(shell docker ps | grep -ci mattermost-mysql) -eq 0 ]; then \ + @if [ $(shell docker ps -a | grep -ci mattermost-mysql) -eq 0 ]; then \ echo restoring go libs using godep; \ $(GOPATH)/bin/godep restore; \ echo starting mattermost-mysql; \ docker run --name mattermost-mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=mostest \ -e MYSQL_USER=mmuser -e MYSQL_PASSWORD=mostest -e MYSQL_DATABASE=mattermost_test -d mysql > /dev/null; \ + elif [ $(shell docker ps | grep -ci mattermost-mysql) -eq 0 ]; then \ + echo restarting mattermost-mysql; \ + docker start mattermost-mysql > /dev/null; \ fi - @if [ $(shell docker ps | grep -ci mattermost-redis) -eq 0 ]; then \ + @if [ $(shell docker ps -a | grep -ci mattermost-redis) -eq 0 ]; then \ echo starting mattermost-redis; \ docker run --name mattermost-redis -p 6379:6379 -d redis > /dev/null; \ + elif [ $(shell docker ps | grep -ci mattermost-redis) -eq 0 ]; then \ + echo restarting mattermost-redis; \ + docker start mattermost-redis > /dev/null; \ fi @cd web/react/ && npm install From c6fb95912bb481791c1ca370a46a4da9c05d05ad Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Wed, 8 Jul 2015 11:50:10 -0400 Subject: [PATCH 88/90] Changing the way we mattermost handles URLs. team.domain.com becomes domain.com/team. Renaming team.Name to team.DisplayName and team.Domain to team.Name. So: team.Name -> url safe name. team.DisplayName -> nice name for users --- api/api.go | 4 +- api/api_test.go | 4 +- api/auto_constants.go | 4 +- api/auto_teams.go | 16 +- api/auto_users.go | 4 +- api/channel_benchmark_test.go | 2 +- api/channel_test.go | 88 +++---- api/command.go | 12 +- api/command_test.go | 12 +- api/context.go | 80 ++++-- api/file.go | 6 +- api/file_test.go | 18 +- api/post.go | 30 +-- api/post_test.go | 56 ++--- api/sharding.go | 79 ++++++ api/team.go | 234 ++++-------------- api/team_test.go | 70 +++--- api/templates/email_change_body.html | 6 +- api/templates/email_change_subject.html | 2 +- api/templates/find_teams_body.html | 4 +- api/templates/invite_body.html | 6 +- api/templates/invite_subject.html | 2 +- api/templates/password_change_body.html | 6 +- api/templates/password_change_subject.html | 2 +- api/templates/post_body.html | 4 +- api/templates/post_subject.html | 2 +- api/templates/reset_body.html | 4 +- api/templates/signup_team_body.html | 4 +- api/templates/verify_body.html | 4 +- api/templates/verify_subject.html | 2 +- api/templates/welcome_body.html | 8 +- api/user.go | 78 +++--- api/user_test.go | 104 ++++---- api/web_socket_test.go | 6 +- config/config.json | 7 +- config/config_docker.json | 7 +- manualtesting/manual_testing.go | 10 +- model/client.go | 20 +- model/team.go | 14 +- model/team_signup_test.go | 2 +- model/team_test.go | 14 +- model/utils.go | 8 +- model/utils_test.go | 8 +- store/sql_team_store.go | 24 +- store/sql_team_store_test.go | 40 +-- store/store.go | 4 +- utils/config.go | 26 +- utils/config_test.go | 15 -- web/react/components/delete_channel_modal.jsx | 2 +- web/react/components/login.jsx | 130 ++-------- web/react/components/msg_typing.jsx | 2 +- web/react/components/navbar.jsx | 104 +------- web/react/components/new_channel.jsx | 6 +- web/react/components/password_reset.jsx | 79 ++---- web/react/components/rename_channel_modal.jsx | 6 +- web/react/components/rename_team_modal.jsx | 8 +- web/react/components/sidebar.jsx | 116 +-------- web/react/components/sidebar_header.jsx | 28 +-- web/react/components/sidebar_right_menu.jsx | 4 +- web/react/components/signup_team.jsx | 11 +- web/react/components/signup_team_complete.jsx | 130 ++++++---- web/react/components/signup_user_complete.jsx | 7 +- web/react/components/team_members.jsx | 2 +- web/react/pages/channel.jsx | 10 +- web/react/pages/home.jsx | 7 +- web/react/pages/login.jsx | 4 +- web/react/pages/password_reset.jsx | 4 +- web/react/pages/signup_team_complete.jsx | 4 +- web/react/stores/browser_store.jsx | 2 +- web/react/stores/post_store.jsx | 12 +- web/react/stores/team_store.jsx | 9 +- web/react/stores/user_store.jsx | 29 ++- web/react/utils/client.jsx | 33 ++- web/react/utils/constants.jsx | 2 +- web/react/utils/utils.jsx | 26 +- web/sass-files/sass/partials/_headers.scss | 5 +- web/sass-files/sass/partials/_signup.scss | 4 +- web/templates/channel.html | 2 +- web/templates/home.html | 2 +- web/templates/login.html | 4 +- web/templates/password_reset.html | 2 +- web/templates/signup_team.html | 3 +- web/templates/signup_team_complete.html | 2 +- web/templates/signup_team_confirm.html | 1 - web/templates/signup_user_complete.html | 2 +- web/web.go | 139 ++++++----- 86 files changed, 891 insertions(+), 1214 deletions(-) create mode 100644 api/sharding.go diff --git a/api/api.go b/api/api.go index 70e1b64aea..25f3376c6f 100644 --- a/api/api.go +++ b/api/api.go @@ -16,10 +16,10 @@ var ServerTemplates *template.Template type ServerTemplatePage Page -func NewServerTemplatePage(templateName, teamUrl string) *ServerTemplatePage { +func NewServerTemplatePage(templateName, teamURL string) *ServerTemplatePage { props := make(map[string]string) props["AnalyticsUrl"] = utils.Cfg.ServiceSettings.AnalyticsUrl - return &ServerTemplatePage{TemplateName: templateName, SiteName: utils.Cfg.ServiceSettings.SiteName, FeedbackEmail: utils.Cfg.EmailSettings.FeedbackEmail, TeamUrl: teamUrl, Props: props} + return &ServerTemplatePage{TemplateName: templateName, SiteName: utils.Cfg.ServiceSettings.SiteName, FeedbackEmail: utils.Cfg.EmailSettings.FeedbackEmail, TeamURL: teamURL, Props: props} } func (me *ServerTemplatePage) Render() string { diff --git a/api/api_test.go b/api/api_test.go index 7d2faed4fc..0c2e57891b 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -24,12 +24,12 @@ func Setup() { func SetupBenchmark() (*model.Team, *model.User, *model.Channel) { Setup() - team := &model.Team{Name: "Benchmark Team", Domain: "z-z-" + model.NewId() + "a", Email: "benchmark@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Benchmark Team", Name: "z-z-" + model.NewId() + "a", Email: "benchmark@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: model.NewId() + "benchmark@test.com", Nickname: "Mr. Benchmarker", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") channel := &model.Channel{DisplayName: "Benchmark Channel", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel) diff --git a/api/auto_constants.go b/api/auto_constants.go index 3f88310552..f80f15f2d2 100644 --- a/api/auto_constants.go +++ b/api/auto_constants.go @@ -12,8 +12,8 @@ const ( USER_PASSWORD = "passwd" CHANNEL_TYPE = model.CHANNEL_OPEN FUZZ_USER_EMAIL_PREFIX_LEN = 10 - BTEST_TEAM_NAME = "TestTeam" - BTEST_TEAM_DOMAIN_NAME = "z-z-testdomaina" + BTEST_TEAM_DISPLAY_NAME = "TestTeam" + BTEST_TEAM_NAME = "z-z-testdomaina" BTEST_TEAM_EMAIL = "test@nowhere.com" BTEST_TEAM_TYPE = model.TEAM_OPEN BTEST_USER_NAME = "Mr. Testing Tester" diff --git a/api/auto_teams.go b/api/auto_teams.go index 2fe8267745..e5c772b4c8 100644 --- a/api/auto_teams.go +++ b/api/auto_teams.go @@ -39,22 +39,22 @@ func NewAutoTeamCreator(client *model.Client) *AutoTeamCreator { func (cfg *AutoTeamCreator) createRandomTeam() (*model.Team, bool) { var teamEmail string + var teamDisplayName string var teamName string - var teamDomain string if cfg.Fuzzy { teamEmail = utils.FuzzEmail() + teamDisplayName = utils.FuzzName() teamName = utils.FuzzName() - teamDomain = utils.FuzzName() } else { teamEmail = utils.RandomEmail(cfg.EmailLength, cfg.EmailCharset) - teamName = utils.RandomName(cfg.NameLength, cfg.NameCharset) - teamDomain = utils.RandomName(cfg.NameLength, cfg.NameCharset) + model.NewId() + teamDisplayName = utils.RandomName(cfg.NameLength, cfg.NameCharset) + teamName = utils.RandomName(cfg.NameLength, cfg.NameCharset) + model.NewId() } team := &model.Team{ - Name: teamName, - Domain: teamDomain, - Email: teamEmail, - Type: model.TEAM_OPEN, + DisplayName: teamDisplayName, + Name: teamName, + Email: teamEmail, + Type: model.TEAM_OPEN, } result, err := cfg.client.CreateTeam(team) diff --git a/api/auto_users.go b/api/auto_users.go index d6918f13ac..39ed201b96 100644 --- a/api/auto_users.go +++ b/api/auto_users.go @@ -33,9 +33,9 @@ func NewAutoUserCreator(client *model.Client, teamID string) *AutoUserCreator { // Basic test team and user so you always know one func CreateBasicUser(client *model.Client) *model.AppError { - result, _ := client.FindTeamByDomain(BTEST_TEAM_DOMAIN_NAME, true) + result, _ := client.FindTeamByName(BTEST_TEAM_NAME, true) if result.Data.(bool) == false { - newteam := &model.Team{Name: BTEST_TEAM_NAME, Domain: BTEST_TEAM_DOMAIN_NAME, Email: BTEST_TEAM_EMAIL, Type: BTEST_TEAM_TYPE} + newteam := &model.Team{DisplayName: BTEST_TEAM_DISPLAY_NAME, Name: BTEST_TEAM_NAME, Email: BTEST_TEAM_EMAIL, Type: BTEST_TEAM_TYPE} result, err := client.CreateTeam(newteam) if err != nil { return err diff --git a/api/channel_benchmark_test.go b/api/channel_benchmark_test.go index 17d3deb274..8816381769 100644 --- a/api/channel_benchmark_test.go +++ b/api/channel_benchmark_test.go @@ -141,7 +141,7 @@ func BenchmarkJoinChannel(b *testing.B) { user := &model.User{TeamId: team.Id, Email: model.NewId() + "random@test.com", Nickname: "That Guy", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") // Benchmark Start b.ResetTimer() diff --git a/api/channel_test.go b/api/channel_test.go index 31ab85117c..ae77813026 100644 --- a/api/channel_test.go +++ b/api/channel_test.go @@ -14,17 +14,17 @@ import ( func TestCreateChannel(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - team2 := &model.Team{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team2 := &model.Team{DisplayName: "Name Team 2", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") channel := model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} rchannel, err := Client.CreateChannel(&channel) @@ -94,7 +94,7 @@ func TestCreateChannel(t *testing.T) { func TestCreateDirectChannel(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -105,7 +105,7 @@ func TestCreateDirectChannel(t *testing.T) { user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") data := make(map[string]string) data["user_id"] = user2.Id @@ -149,7 +149,7 @@ func TestCreateDirectChannel(t *testing.T) { func TestUpdateChannel(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"} @@ -165,7 +165,7 @@ func TestUpdateChannel(t *testing.T) { store.Must(Srv.Store.User().VerifyEmail(userStd.Id)) userStd.Roles = "" - Client.LoginByEmail(team.Domain, userChannelAdmin.Email, "pwd") + Client.LoginByEmail(team.Name, userChannelAdmin.Email, "pwd") channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -184,7 +184,7 @@ func TestUpdateChannel(t *testing.T) { t.Fatal("Channel admin failed to skip displayName") } - Client.LoginByEmail(team.Domain, userTeamAdmin.Email, "pwd") + Client.LoginByEmail(team.Name, userTeamAdmin.Email, "pwd") desc = "b" + model.NewId() + "b" upChannel1 = &model.Channel{Id: channel1.Id, Description: desc} @@ -210,7 +210,7 @@ func TestUpdateChannel(t *testing.T) { } } - Client.LoginByEmail(team.Domain, userStd.Email, "pwd") + Client.LoginByEmail(team.Name, userStd.Email, "pwd") if _, err := Client.UpdateChannel(upChannel1); err == nil { t.Fatal("Standard User should have failed to update") @@ -220,14 +220,14 @@ func TestUpdateChannel(t *testing.T) { func TestUpdateChannelDesc(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -270,7 +270,7 @@ func TestUpdateChannelDesc(t *testing.T) { user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - Client.LoginByEmail(team.Domain, user2.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, "pwd") data["channel_id"] = channel1.Id data["channel_description"] = "new desc" @@ -282,14 +282,14 @@ func TestUpdateChannelDesc(t *testing.T) { func TestGetChannel(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -324,14 +324,14 @@ func TestGetChannel(t *testing.T) { func TestGetMoreChannel(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -343,7 +343,7 @@ func TestGetMoreChannel(t *testing.T) { user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - Client.LoginByEmail(team.Domain, user2.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, "pwd") rget := Client.Must(Client.GetMoreChannels("")) data := rget.Data.(*model.ChannelList) @@ -368,14 +368,14 @@ func TestGetMoreChannel(t *testing.T) { func TestJoinChannel(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -387,7 +387,7 @@ func TestJoinChannel(t *testing.T) { user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - Client.LoginByEmail(team.Domain, user2.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, "pwd") Client.Must(Client.JoinChannel(channel1.Id)) @@ -402,7 +402,7 @@ func TestJoinChannel(t *testing.T) { user3 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) - Client.LoginByEmail(team.Domain, user3.Email, "pwd") + Client.LoginByEmail(team.Name, user3.Email, "pwd") if _, err := Client.JoinChannel(rchannel.Id); err == nil { t.Fatal("shoudn't be able to join direct channel") @@ -412,14 +412,14 @@ func TestJoinChannel(t *testing.T) { func TestLeaveChannel(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -431,7 +431,7 @@ func TestLeaveChannel(t *testing.T) { user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - Client.LoginByEmail(team.Domain, user2.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, "pwd") Client.Must(Client.JoinChannel(channel1.Id)) @@ -461,7 +461,7 @@ func TestLeaveChannel(t *testing.T) { func TestDeleteChannel(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"} @@ -472,14 +472,14 @@ func TestDeleteChannel(t *testing.T) { userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id)) - Client.LoginByEmail(team.Domain, userChannelAdmin.Email, "pwd") + Client.LoginByEmail(team.Name, userChannelAdmin.Email, "pwd") channelMadeByCA := &model.Channel{DisplayName: "C Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channelMadeByCA = Client.Must(Client.CreateChannel(channelMadeByCA)).Data.(*model.Channel) Client.AddChannelMember(channelMadeByCA.Id, userTeamAdmin.Id) - Client.LoginByEmail(team.Domain, userTeamAdmin.Email, "pwd") + Client.LoginByEmail(team.Name, userTeamAdmin.Email, "pwd") channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -504,7 +504,7 @@ func TestDeleteChannel(t *testing.T) { userStd = Client.Must(Client.CreateUser(userStd, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(userStd.Id)) - Client.LoginByEmail(team.Domain, userStd.Email, "pwd") + Client.LoginByEmail(team.Name, userStd.Email, "pwd") if _, err := Client.JoinChannel(channel1.Id); err == nil { t.Fatal("should have failed to join deleted channel") @@ -531,14 +531,14 @@ func TestDeleteChannel(t *testing.T) { func TestGetChannelExtraInfo(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -552,14 +552,14 @@ func TestGetChannelExtraInfo(t *testing.T) { func TestAddChannelMember(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -591,13 +591,13 @@ func TestAddChannelMember(t *testing.T) { channel2 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) - Client.LoginByEmail(team.Domain, user2.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, "pwd") if _, err := Client.AddChannelMember(channel2.Id, user2.Id); err == nil { t.Fatal("Should have errored, user not in channel") } - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") Client.Must(Client.DeleteChannel(channel2.Id)) @@ -610,7 +610,7 @@ func TestAddChannelMember(t *testing.T) { func TestRemoveChannelMember(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) userTeamAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"} @@ -621,14 +621,14 @@ func TestRemoveChannelMember(t *testing.T) { userChannelAdmin = Client.Must(Client.CreateUser(userChannelAdmin, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(userChannelAdmin.Id)) - Client.LoginByEmail(team.Domain, userChannelAdmin.Email, "pwd") + Client.LoginByEmail(team.Name, userChannelAdmin.Email, "pwd") channelMadeByCA := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channelMadeByCA = Client.Must(Client.CreateChannel(channelMadeByCA)).Data.(*model.Channel) Client.Must(Client.AddChannelMember(channelMadeByCA.Id, userTeamAdmin.Id)) - Client.LoginByEmail(team.Domain, userTeamAdmin.Email, "pwd") + Client.LoginByEmail(team.Name, userTeamAdmin.Email, "pwd") channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -660,13 +660,13 @@ func TestRemoveChannelMember(t *testing.T) { channel2 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) - Client.LoginByEmail(team.Domain, userStd.Email, "pwd") + Client.LoginByEmail(team.Name, userStd.Email, "pwd") if _, err := Client.RemoveChannelMember(channel2.Id, userStd.Id); err == nil { t.Fatal("Should have errored, user not channel admin") } - Client.LoginByEmail(team.Domain, userTeamAdmin.Email, "pwd") + Client.LoginByEmail(team.Name, userTeamAdmin.Email, "pwd") Client.Must(Client.AddChannelMember(channel2.Id, userStd.Id)) Client.Must(Client.DeleteChannel(channel2.Id)) @@ -680,14 +680,14 @@ func TestRemoveChannelMember(t *testing.T) { func TestUpdateNotifyLevel(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") channel1 := &model.Channel{DisplayName: "A Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -749,7 +749,7 @@ func TestUpdateNotifyLevel(t *testing.T) { user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - Client.LoginByEmail(team.Domain, user2.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, "pwd") data["channel_id"] = channel1.Id data["user_id"] = user2.Id @@ -762,14 +762,14 @@ func TestUpdateNotifyLevel(t *testing.T) { func TestFuzzyChannel(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") // Strings that should pass as acceptable channel names var fuzzyStringsPass = []string{ diff --git a/api/command.go b/api/command.go index ee7a11af35..f051bd42e7 100644 --- a/api/command.go +++ b/api/command.go @@ -321,14 +321,14 @@ func loadTestSetupCommand(c *Context, command *model.Command) bool { numPosts, _ = strconv.Atoi(tokens[numArgs+2]) } } - client := model.NewClient(c.TeamUrl + "/api/v1") + client := model.NewClient(c.GetSiteURL() + "/api/v1") if doTeams { if err := CreateBasicUser(client); err != nil { l4g.Error("Failed to create testing enviroment") return true } - client.LoginByEmail(BTEST_TEAM_DOMAIN_NAME, BTEST_USER_EMAIL, BTEST_USER_PASSWORD) + client.LoginByEmail(BTEST_TEAM_NAME, BTEST_USER_EMAIL, BTEST_USER_PASSWORD) enviroment, err := CreateTestEnviromentWithTeams( client, utils.Range{numTeams, numTeams}, @@ -342,7 +342,7 @@ func loadTestSetupCommand(c *Context, command *model.Command) bool { } else { l4g.Info("Testing enviroment created") for i := 0; i < len(enviroment.Teams); i++ { - l4g.Info("Team Created: " + enviroment.Teams[i].Domain) + l4g.Info("Team Created: " + enviroment.Teams[i].Name) l4g.Info("\t User to login: " + enviroment.Enviroments[i].Users[0].Email + ", " + USER_PASSWORD) } } @@ -381,7 +381,7 @@ func loadTestUsersCommand(c *Context, command *model.Command) bool { if err == false { usersr = utils.Range{10, 15} } - client := model.NewClient(c.TeamUrl + "/api/v1") + client := model.NewClient(c.GetSiteURL() + "/api/v1") userCreator := NewAutoUserCreator(client, c.Session.TeamId) userCreator.Fuzzy = doFuzz userCreator.CreateTestUsers(usersr) @@ -411,7 +411,7 @@ func loadTestChannelsCommand(c *Context, command *model.Command) bool { if err == false { channelsr = utils.Range{20, 30} } - client := model.NewClient(c.TeamUrl + "/api/v1") + client := model.NewClient(c.GetSiteURL() + "/api/v1") client.MockSession(c.Session.Id) channelCreator := NewAutoChannelCreator(client, c.Session.TeamId) channelCreator.Fuzzy = doFuzz @@ -463,7 +463,7 @@ func loadTestPostsCommand(c *Context, command *model.Command) bool { } } - client := model.NewClient(c.TeamUrl + "/api/v1") + client := model.NewClient(c.GetSiteURL() + "/api/v1") client.MockSession(c.Session.Id) testPoster := NewAutoPostCreator(client, command.ChannelId) testPoster.Fuzzy = doFuzz diff --git a/api/command_test.go b/api/command_test.go index d9912f9d8b..a58ef9be53 100644 --- a/api/command_test.go +++ b/api/command_test.go @@ -12,14 +12,14 @@ import ( func TestSuggestRootCommands(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") if _, err := Client.Command("", "", true); err == nil { t.Fatal("Should fail") @@ -55,14 +55,14 @@ func TestSuggestRootCommands(t *testing.T) { func TestLogoutCommands(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") rs1 := Client.Must(Client.Command("", "/logout", false)).Data.(*model.Command) if rs1.GotoLocation != "/logout" { @@ -73,14 +73,14 @@ func TestLogoutCommands(t *testing.T) { func TestJoinCommands(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) diff --git a/api/context.go b/api/context.go index 054e42e2ed..ac9dffcbc9 100644 --- a/api/context.go +++ b/api/context.go @@ -17,12 +17,14 @@ import ( var sessionCache *utils.Cache = utils.NewLru(model.SESSION_CACHE_SIZE) type Context struct { - Session model.Session - RequestId string - IpAddress string - TeamUrl string - Path string - Err *model.AppError + Session model.Session + RequestId string + IpAddress string + Path string + Err *model.AppError + teamURLValid bool + teamURL string + siteURL string } type Page struct { @@ -30,32 +32,36 @@ type Page struct { Title string SiteName string FeedbackEmail string - TeamUrl string + TeamURL string Props map[string]string } func ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { - return &handler{h, false, false, true, false} + return &handler{h, false, false, true, false, false} } func AppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { - return &handler{h, false, false, false, false} + return &handler{h, false, false, false, false, false} +} + +func AppHandlerIndependent(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { + return &handler{h, false, false, false, false, true} } func ApiUserRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { - return &handler{h, true, false, true, true} + return &handler{h, true, false, true, true, false} } func ApiUserRequiredActivity(h func(*Context, http.ResponseWriter, *http.Request), isUserActivity bool) http.Handler { - return &handler{h, true, false, true, isUserActivity} + return &handler{h, true, false, true, isUserActivity, false} } func UserRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { - return &handler{h, true, false, false, false} + return &handler{h, true, false, false, false, false} } func ApiAdminSystemRequired(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler { - return &handler{h, true, true, true, false} + return &handler{h, true, true, true, false, false} } type handler struct { @@ -64,6 +70,7 @@ type handler struct { requireSystemAdmin bool isApi bool isUserActivity bool + isTeamIndependent bool } func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -73,7 +80,6 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { c := &Context{} c.RequestId = model.NewId() c.IpAddress = GetIpAddress(r) - c.Path = r.URL.Path protocol := "http" @@ -90,7 +96,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } - c.TeamUrl = protocol + "://" + r.Host + c.setSiteURL(protocol + "://" + r.Host) w.Header().Set(model.HEADER_REQUEST_ID, c.RequestId) w.Header().Set(model.HEADER_VERSION_ID, utils.Cfg.ServiceSettings.Version) @@ -135,6 +141,15 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } + if h.isApi || h.isTeamIndependent { + c.setTeamURL(c.GetSiteURL(), false) + c.Path = r.URL.Path + } else { + splitURL := strings.Split(r.URL.Path, "/") + c.setTeamURL(protocol+"://"+r.Host+"/"+splitURL[1], true) + c.Path = "/" + strings.Join(splitURL[2:], "/") + } + if c.Err == nil && h.requireUser { c.UserRequired() } @@ -165,7 +180,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write([]byte(c.Err.ToJson())) } else { if c.Err.StatusCode == http.StatusUnauthorized { - http.Redirect(w, r, "/?redirect="+url.QueryEscape(r.URL.Path), http.StatusTemporaryRedirect) + http.Redirect(w, r, c.GetTeamURL()+"/?redirect="+url.QueryEscape(r.URL.Path), http.StatusTemporaryRedirect) } else { RenderWebError(c.Err, w, r) } @@ -299,6 +314,39 @@ func (c *Context) SetUnknownError(where string, details string) { c.Err = model.NewAppError(where, "An unknown error has occured. Please contact support.", details) } +func (c *Context) setTeamURL(url string, valid bool) { + c.teamURL = url + c.teamURLValid = valid +} + +func (c *Context) setTeamURLFromSession() { + if result := <-Srv.Store.Team().Get(c.Session.TeamId); result.Err == nil { + c.setTeamURL(c.GetSiteURL()+"/"+result.Data.(*model.Team).Name, true) + } +} + +func (c *Context) setSiteURL(url string) { + c.siteURL = url +} + +func (c *Context) GetTeamURLFromTeam(team *model.Team) string { + return c.GetSiteURL() + "/" + team.Name +} + +func (c *Context) GetTeamURL() string { + if !c.teamURLValid { + c.setTeamURLFromSession() + if !c.teamURLValid { + l4g.Debug("TeamURL accessed when not valid. Team URL should not be used in api functions or those that are team independent") + } + } + return c.teamURL +} + +func (c *Context) GetSiteURL() string { + return c.siteURL +} + func GetIpAddress(r *http.Request) string { address := r.Header.Get(model.HEADER_FORWARDED) if len(address) == 0 { diff --git a/api/file.go b/api/file.go index 0e08567d65..362cdf896b 100644 --- a/api/file.go +++ b/api/file.go @@ -13,9 +13,9 @@ import ( "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" "github.com/nfnt/resize" + _ "golang.org/x/image/bmp" "image" _ "image/gif" - _ "golang.org/x/image/bmp" "image/jpeg" "io" "net/http" @@ -115,7 +115,7 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) { return } - fileUrl := c.TeamUrl + "/api/v1/files/get/" + channelId + "/" + c.Session.UserId + "/" + uid + "/" + url.QueryEscape(files[i].Filename) + fileUrl := c.GetSiteURL() + "/api/v1/files/get/" + channelId + "/" + c.Session.UserId + "/" + uid + "/" + url.QueryEscape(files[i].Filename) resStruct.Filenames = append(resStruct.Filenames, fileUrl) } @@ -363,7 +363,7 @@ func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) { data := model.MapToJson(newProps) hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt)) - url := fmt.Sprintf("%s/api/v1/files/%s/%s/%s/%s?d=%s&h=%s&t=%s", c.TeamUrl, getType, channelId, userId, filename, url.QueryEscape(data), url.QueryEscape(hash), c.Session.TeamId) + url := fmt.Sprintf("%s/api/v1/files/%s/%s/%s/%s?d=%s&h=%s&t=%s", c.GetSiteURL(), getType, channelId, userId, filename, url.QueryEscape(data), url.QueryEscape(hash), c.Session.TeamId) if !c.HasPermissionsToChannel(cchan, "getPublicLink") { return diff --git a/api/file_test.go b/api/file_test.go index 044cad921d..79ee03c778 100644 --- a/api/file_test.go +++ b/api/file_test.go @@ -24,14 +24,14 @@ import ( func TestUploadFile(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -111,14 +111,14 @@ func TestUploadFile(t *testing.T) { func TestGetFile(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -173,7 +173,7 @@ func TestGetFile(t *testing.T) { t.Fatal("file get failed") } - team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team2 := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) user2 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -187,7 +187,7 @@ func TestGetFile(t *testing.T) { data := model.MapToJson(newProps) hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.PublicLinkSalt)) - Client.LoginByEmail(team2.Domain, user2.Email, "pwd") + Client.LoginByEmail(team2.Name, user2.Email, "pwd") if _, downErr := Client.GetFile(filenames[0]+"?d="+url.QueryEscape(data)+"&h="+url.QueryEscape(hash)+"&t="+team.Id, true); downErr != nil { t.Fatal(downErr) @@ -258,7 +258,7 @@ func TestGetFile(t *testing.T) { func TestGetPublicLink(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -269,7 +269,7 @@ func TestGetPublicLink(t *testing.T) { user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -344,7 +344,7 @@ func TestGetPublicLink(t *testing.T) { t.Fatal("Should have errored - bad file path") } - Client.LoginByEmail(team.Domain, user2.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, "pwd") data["filename"] = filenames[0] if _, err := Client.GetPublicLink(data); err == nil { t.Fatal("should have errored, user not member of channel") diff --git a/api/post.go b/api/post.go index 27dedbf714..fb9fdd1eff 100644 --- a/api/post.go +++ b/api/post.go @@ -122,7 +122,7 @@ func CreateValetPost(c *Context, post *model.Post) (*model.Post, *model.AppError rpost = result.Data.(*model.Post) } - fireAndForgetNotifications(rpost, c.Session.TeamId, c.TeamUrl) + fireAndForgetNotifications(rpost, c.Session.TeamId, c.GetSiteURL()) return rpost, nil } @@ -201,14 +201,14 @@ func CreatePost(c *Context, post *model.Post, doUpdateLastViewed bool) (*model.P } else { rpost = result.Data.(*model.Post) - fireAndForgetNotifications(rpost, c.Session.TeamId, c.TeamUrl) + fireAndForgetNotifications(rpost, c.Session.TeamId, c.GetSiteURL()) } return rpost, nil } -func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) { +func fireAndForgetNotifications(post *model.Post, teamId, siteURL string) { go func() { // Get a list of user names (to be used as keywords) and ids for the given team @@ -362,20 +362,22 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) { mentionedUsers = append(mentionedUsers, k) } - var teamName string + var teamDisplayName string + var teamURL string if result := <-tchan; result.Err != nil { l4g.Error("Failed to retrieve team team_id=%v, err=%v", teamId, result.Err) return } else { - teamName = result.Data.(*model.Team).Name + teamDisplayName = result.Data.(*model.Team).DisplayName + teamURL = siteURL + "/" + result.Data.(*model.Team).Name } // Build and send the emails location, _ := time.LoadLocation("UTC") tm := time.Unix(post.CreateAt/1000, 0).In(location) - subjectPage := NewServerTemplatePage("post_subject", teamUrl) - subjectPage.Props["TeamName"] = teamName + subjectPage := NewServerTemplatePage("post_subject", teamURL) + subjectPage.Props["TeamDisplayName"] = teamDisplayName subjectPage.Props["SubjectText"] = subjectText subjectPage.Props["Month"] = tm.Month().String()[:3] subjectPage.Props["Day"] = fmt.Sprintf("%d", tm.Day()) @@ -392,9 +394,9 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) { continue } - bodyPage := NewServerTemplatePage("post_body", teamUrl) + bodyPage := NewServerTemplatePage("post_body", teamURL) bodyPage.Props["Nickname"] = profileMap[id].FirstName - bodyPage.Props["TeamName"] = teamName + bodyPage.Props["TeamDisplayName"] = teamDisplayName bodyPage.Props["ChannelName"] = channelName bodyPage.Props["BodyText"] = bodyText bodyPage.Props["SenderName"] = senderName @@ -403,7 +405,7 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) { bodyPage.Props["Month"] = tm.Month().String()[:3] bodyPage.Props["Day"] = fmt.Sprintf("%d", tm.Day()) bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message) - bodyPage.Props["TeamLink"] = teamUrl + "/channels/" + channel.Name + bodyPage.Props["TeamLink"] = teamURL + "/channels/" + channel.Name if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil { l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err) @@ -636,9 +638,9 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { post := result.Data.(*model.PostList).Posts[postId] - if !c.HasPermissionsToChannel(cchan, "deletePost") && !c.IsTeamAdmin(post.UserId){ - return - } + if !c.HasPermissionsToChannel(cchan, "deletePost") && !c.IsTeamAdmin(post.UserId) { + return + } if post == nil { c.SetInvalidParam("deletePost", "postId") @@ -651,7 +653,7 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { return } - if post.UserId != c.Session.UserId && !strings.Contains(c.Session.Roles,model.ROLE_ADMIN) { + if post.UserId != c.Session.UserId && !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) { c.Err = model.NewAppError("deletePost", "You do not have the appropriate permissions", "") c.Err.StatusCode = http.StatusForbidden return diff --git a/api/post_test.go b/api/post_test.go index 583d1be43d..3249ed7faf 100644 --- a/api/post_test.go +++ b/api/post_test.go @@ -15,10 +15,10 @@ import ( func TestCreatePost(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - team2 := &model.Team{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team2 := &model.Team{DisplayName: "Name Team 2", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -29,7 +29,7 @@ func TestCreatePost(t *testing.T) { user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -96,7 +96,7 @@ func TestCreatePost(t *testing.T) { t.Fatal("Should have been forbidden") } - Client.LoginByEmail(team.Domain, user2.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, "pwd") post7 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} _, err = Client.CreatePost(post7) if err.StatusCode != http.StatusForbidden { @@ -107,7 +107,7 @@ func TestCreatePost(t *testing.T) { user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user3.Id)) - Client.LoginByEmail(team2.Domain, user3.Email, "pwd") + Client.LoginByEmail(team2.Name, user3.Email, "pwd") channel3 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team2.Id} channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel) @@ -126,10 +126,10 @@ func TestCreatePost(t *testing.T) { func TestCreateValetPost(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - team2 := &model.Team{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team2 := &model.Team{DisplayName: "Name Team 2", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -140,7 +140,7 @@ func TestCreateValetPost(t *testing.T) { user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -181,7 +181,7 @@ func TestCreateValetPost(t *testing.T) { t.Fatal("Should have been forbidden") } - Client.LoginByEmail(team.Domain, user2.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, "pwd") post5 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} _, err = Client.CreateValetPost(post5) if err != nil { @@ -192,7 +192,7 @@ func TestCreateValetPost(t *testing.T) { user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user3.Id)) - Client.LoginByEmail(team2.Domain, user3.Email, "pwd") + Client.LoginByEmail(team2.Name, user3.Email, "pwd") channel3 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team2.Id} channel3 = Client.Must(Client.CreateChannel(channel3)).Data.(*model.Channel) @@ -218,10 +218,10 @@ func TestCreateValetPost(t *testing.T) { func TestUpdatePost(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) - team2 := &model.Team{Name: "Name Team 2", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team2 := &model.Team{DisplayName: "Name Team 2", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -232,7 +232,7 @@ func TestUpdatePost(t *testing.T) { user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -289,14 +289,14 @@ func TestUpdatePost(t *testing.T) { func TestGetPosts(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -354,14 +354,14 @@ func TestGetPosts(t *testing.T) { func TestSearchPosts(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -400,14 +400,14 @@ func TestSearchPosts(t *testing.T) { func TestSearchHashtagPosts(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -431,14 +431,14 @@ func TestSearchHashtagPosts(t *testing.T) { func TestGetPostsCache(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -480,7 +480,7 @@ func TestGetPostsCache(t *testing.T) { func TestDeletePosts(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) userAdmin := &model.User{TeamId: team.Id, Email: team.Email, Nickname: "Corey Hulen", Password: "pwd"} @@ -491,7 +491,7 @@ func TestDeletePosts(t *testing.T) { user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -532,7 +532,7 @@ func TestDeletePosts(t *testing.T) { post4 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"} post4 = Client.Must(Client.CreatePost(post4)).Data.(*model.Post) - Client.LoginByEmail(team.Domain, userAdmin.Email, "pwd") + Client.LoginByEmail(team.Name, userAdmin.Email, "pwd") Client.Must(Client.DeletePost(channel1.Id, post4.Id)) } @@ -540,14 +540,14 @@ func TestDeletePosts(t *testing.T) { func TestEmailMention(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: "corey@test.com", Nickname: "Bob Bobby", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "TestGetPosts", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -562,14 +562,14 @@ func TestEmailMention(t *testing.T) { func TestFuzzyPosts(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) diff --git a/api/sharding.go b/api/sharding.go new file mode 100644 index 0000000000..569d3a232e --- /dev/null +++ b/api/sharding.go @@ -0,0 +1,79 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +package api + +/* +func createSubDomain(subDomain string, target string) { + + if utils.Cfg.AWSSettings.Route53AccessKeyId == "" { + return + } + + creds := aws.Creds(utils.Cfg.AWSSettings.Route53AccessKeyId, utils.Cfg.AWSSettings.Route53SecretAccessKey, "") + r53 := route53.New(aws.DefaultConfig.Merge(&aws.Config{Credentials: creds, Region: utils.Cfg.AWSSettings.Route53Region})) + + rr := route53.ResourceRecord{ + Value: aws.String(target), + } + + rrs := make([]*route53.ResourceRecord, 1) + rrs[0] = &rr + + change := route53.Change{ + Action: aws.String("CREATE"), + ResourceRecordSet: &route53.ResourceRecordSet{ + Name: aws.String(fmt.Sprintf("%v.%v", subDomain, utils.Cfg.ServiceSettings.Domain)), + TTL: aws.Long(300), + Type: aws.String("CNAME"), + ResourceRecords: rrs, + }, + } + + changes := make([]*route53.Change, 1) + changes[0] = &change + + r53req := &route53.ChangeResourceRecordSetsInput{ + HostedZoneID: aws.String(utils.Cfg.AWSSettings.Route53ZoneId), + ChangeBatch: &route53.ChangeBatch{ + Changes: changes, + }, + } + + if _, err := r53.ChangeResourceRecordSets(r53req); err != nil { + l4g.Error("erro in createSubDomain domain=%v err=%v", subDomain, err) + return + } +} + +func doesSubDomainExist(subDomain string) bool { + + // if it's configured for testing then skip this step + if utils.Cfg.AWSSettings.Route53AccessKeyId == "" { + return false + } + + creds := aws.Creds(utils.Cfg.AWSSettings.Route53AccessKeyId, utils.Cfg.AWSSettings.Route53SecretAccessKey, "") + r53 := route53.New(aws.DefaultConfig.Merge(&aws.Config{Credentials: creds, Region: utils.Cfg.AWSSettings.Route53Region})) + + r53req := &route53.ListResourceRecordSetsInput{ + HostedZoneID: aws.String(utils.Cfg.AWSSettings.Route53ZoneId), + MaxItems: aws.String("1"), + StartRecordName: aws.String(fmt.Sprintf("%v.%v.", subDomain, utils.Cfg.ServiceSettings.Domain)), + } + + if result, err := r53.ListResourceRecordSets(r53req); err != nil { + l4g.Error("error in doesSubDomainExist domain=%v err=%v", subDomain, err) + return true + } else { + + for _, v := range result.ResourceRecordSets { + if v.Name != nil && *v.Name == fmt.Sprintf("%v.%v.", subDomain, utils.Cfg.ServiceSettings.Domain) { + return true + } + } + } + + return false +} +*/ diff --git a/api/team.go b/api/team.go index 7a4cabf7e1..1145e6e81c 100644 --- a/api/team.go +++ b/api/team.go @@ -6,8 +6,6 @@ package api import ( l4g "code.google.com/p/log4go" "fmt" - "github.com/awslabs/aws-sdk-go/aws" - "github.com/awslabs/aws-sdk-go/service/route53" "github.com/gorilla/mux" "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" @@ -24,11 +22,11 @@ func InitTeam(r *mux.Router) { sr.Handle("/create", ApiAppHandler(createTeam)).Methods("POST") sr.Handle("/create_from_signup", ApiAppHandler(createTeamFromSignup)).Methods("POST") sr.Handle("/signup", ApiAppHandler(signupTeam)).Methods("POST") - sr.Handle("/find_team_by_domain", ApiAppHandler(findTeamByDomain)).Methods("POST") + sr.Handle("/find_team_by_name", ApiAppHandler(findTeamByName)).Methods("POST") sr.Handle("/find_teams", ApiAppHandler(findTeams)).Methods("POST") sr.Handle("/email_teams", ApiAppHandler(emailTeams)).Methods("POST") sr.Handle("/invite_members", ApiUserRequired(inviteMembers)).Methods("POST") - sr.Handle("/update_name", ApiUserRequired(updateTeamName)).Methods("POST") + sr.Handle("/update_name", ApiUserRequired(updateTeamDisplayName)).Methods("POST") sr.Handle("/update_valet_feature", ApiUserRequired(updateValetFeature)).Methods("POST") sr.Handle("/me", ApiUserRequired(getMyTeam)).Methods("GET") } @@ -37,31 +35,31 @@ func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) { m := model.MapFromJson(r.Body) email := strings.ToLower(strings.TrimSpace(m["email"])) - name := strings.TrimSpace(m["name"]) + displayName := strings.TrimSpace(m["display_name"]) if len(email) == 0 { c.SetInvalidParam("signupTeam", "email") return } - if len(name) == 0 { - c.SetInvalidParam("signupTeam", "name") + if len(displayName) == 0 { + c.SetInvalidParam("signupTeam", "display_name") return } - subjectPage := NewServerTemplatePage("signup_team_subject", c.TeamUrl) - bodyPage := NewServerTemplatePage("signup_team_body", c.TeamUrl) + subjectPage := NewServerTemplatePage("signup_team_subject", c.GetSiteURL()) + bodyPage := NewServerTemplatePage("signup_team_body", c.GetSiteURL()) bodyPage.Props["TourUrl"] = utils.Cfg.TeamSettings.TourLink props := make(map[string]string) props["email"] = email - props["name"] = name + props["display_name"] = displayName props["time"] = fmt.Sprintf("%v", model.GetMillis()) data := model.MapToJson(props) hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt)) - bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_team_complete/?d=%s&h=%s", c.TeamUrl, url.QueryEscape(data), url.QueryEscape(hash)) + bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_team_complete/?d=%s&h=%s", c.GetSiteURL(), url.QueryEscape(data), url.QueryEscape(hash)) if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil { c.Err = err @@ -119,25 +117,16 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) { return } - found := FindTeamByDomain(c, teamSignup.Team.Domain, "true") + found := FindTeamByName(c, teamSignup.Team.Name, "true") if c.Err != nil { return } if found { - c.Err = model.NewAppError("createTeamFromSignup", "This URL is unavailable. Please try another.", "d="+teamSignup.Team.Domain) + c.Err = model.NewAppError("createTeamFromSignup", "This URL is unavailable. Please try another.", "d="+teamSignup.Team.Name) return } - if IsBetaDomain(r) { - for key, value := range utils.Cfg.ServiceSettings.Shards { - if strings.Index(r.Host, key) == 0 { - createSubDomain(teamSignup.Team.Domain, value) - break - } - } - } - teamSignup.Team.AllowValet = utils.Cfg.TeamSettings.AllowValetDefault if result := <-Srv.Store.Team().Save(&teamSignup.Team); result.Err != nil { @@ -166,7 +155,7 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) { } } - InviteMembers(rteam, ruser, teamSignup.Invites) + InviteMembers(c, rteam, ruser, teamSignup.Invites) teamSignup.Team = *rteam teamSignup.User = *ruser @@ -211,87 +200,14 @@ func createTeam(c *Context, w http.ResponseWriter, r *http.Request) { } } -func doesSubDomainExist(subDomain string) bool { - - // if it's configured for testing then skip this step - if utils.Cfg.AWSSettings.Route53AccessKeyId == "" { - return false - } - - creds := aws.Creds(utils.Cfg.AWSSettings.Route53AccessKeyId, utils.Cfg.AWSSettings.Route53SecretAccessKey, "") - r53 := route53.New(aws.DefaultConfig.Merge(&aws.Config{Credentials: creds, Region: utils.Cfg.AWSSettings.Route53Region})) - - r53req := &route53.ListResourceRecordSetsInput{ - HostedZoneID: aws.String(utils.Cfg.AWSSettings.Route53ZoneId), - MaxItems: aws.String("1"), - StartRecordName: aws.String(fmt.Sprintf("%v.%v.", subDomain, utils.Cfg.ServiceSettings.Domain)), - } - - if result, err := r53.ListResourceRecordSets(r53req); err != nil { - l4g.Error("error in doesSubDomainExist domain=%v err=%v", subDomain, err) - return true - } else { - - for _, v := range result.ResourceRecordSets { - if v.Name != nil && *v.Name == fmt.Sprintf("%v.%v.", subDomain, utils.Cfg.ServiceSettings.Domain) { - return true - } - } - } - - return false -} - -func createSubDomain(subDomain string, target string) { - - if utils.Cfg.AWSSettings.Route53AccessKeyId == "" { - return - } - - creds := aws.Creds(utils.Cfg.AWSSettings.Route53AccessKeyId, utils.Cfg.AWSSettings.Route53SecretAccessKey, "") - r53 := route53.New(aws.DefaultConfig.Merge(&aws.Config{Credentials: creds, Region: utils.Cfg.AWSSettings.Route53Region})) - - rr := route53.ResourceRecord{ - Value: aws.String(target), - } - - rrs := make([]*route53.ResourceRecord, 1) - rrs[0] = &rr - - change := route53.Change{ - Action: aws.String("CREATE"), - ResourceRecordSet: &route53.ResourceRecordSet{ - Name: aws.String(fmt.Sprintf("%v.%v", subDomain, utils.Cfg.ServiceSettings.Domain)), - TTL: aws.Long(300), - Type: aws.String("CNAME"), - ResourceRecords: rrs, - }, - } - - changes := make([]*route53.Change, 1) - changes[0] = &change - - r53req := &route53.ChangeResourceRecordSetsInput{ - HostedZoneID: aws.String(utils.Cfg.AWSSettings.Route53ZoneId), - ChangeBatch: &route53.ChangeBatch{ - Changes: changes, - }, - } - - if _, err := r53.ChangeResourceRecordSets(r53req); err != nil { - l4g.Error("erro in createSubDomain domain=%v err=%v", subDomain, err) - return - } -} - -func findTeamByDomain(c *Context, w http.ResponseWriter, r *http.Request) { +func findTeamByName(c *Context, w http.ResponseWriter, r *http.Request) { m := model.MapFromJson(r.Body) - domain := strings.ToLower(strings.TrimSpace(m["domain"])) + name := strings.ToLower(strings.TrimSpace(m["name"])) all := strings.ToLower(strings.TrimSpace(m["all"])) - found := FindTeamByDomain(c, domain, all) + found := FindTeamByName(c, name, all) if c.Err != nil { return @@ -304,56 +220,25 @@ func findTeamByDomain(c *Context, w http.ResponseWriter, r *http.Request) { } } -func FindTeamByDomain(c *Context, domain string, all string) bool { +func FindTeamByName(c *Context, name string, all string) bool { - if domain == "" || len(domain) > 64 { - c.SetInvalidParam("findTeamByDomain", "domain") + if name == "" || len(name) > 64 { + c.SetInvalidParam("findTeamByName", "domain") return false } - if model.IsReservedDomain(domain) { - c.Err = model.NewAppError("findTeamByDomain", "This URL is unavailable. Please try another.", "d="+domain) + if model.IsReservedTeamName(name) { + c.Err = model.NewAppError("findTeamByName", "This URL is unavailable. Please try another.", "name="+name) return false } - if all == "false" { - if result := <-Srv.Store.Team().GetByDomain(domain); result.Err != nil { - return false - } else { - return true - } + if result := <-Srv.Store.Team().GetByName(name); result.Err != nil { + return false } else { - if doesSubDomainExist(domain) { - return true - } - - protocol := "http" - - if utils.Cfg.ServiceSettings.UseSSL { - protocol = "https" - } - - for key, _ := range utils.Cfg.ServiceSettings.Shards { - url := fmt.Sprintf("%v://%v.%v/api/v1", protocol, key, utils.Cfg.ServiceSettings.Domain) - - if strings.Index(utils.Cfg.ServiceSettings.Domain, "localhost") == 0 { - url = fmt.Sprintf("%v://%v/api/v1", protocol, utils.Cfg.ServiceSettings.Domain) - } - - client := model.NewClient(url) - - if result, err := client.FindTeamByDomain(domain, false); err != nil { - c.Err = err - return false - } else { - if result.Data.(bool) { - return true - } - } - } - - return false + return true } + + return false } func findTeams(c *Context, w http.ResponseWriter, r *http.Request) { @@ -376,7 +261,7 @@ func findTeams(c *Context, w http.ResponseWriter, r *http.Request) { s := make([]string, 0, len(teams)) for _, v := range teams { - s = append(s, v.Domain) + s = append(s, v.Name) } w.Write([]byte(model.ArrayToJson(s))) @@ -394,33 +279,8 @@ func emailTeams(c *Context, w http.ResponseWriter, r *http.Request) { return } - protocol := "http" - - if utils.Cfg.ServiceSettings.UseSSL { - protocol = "https" - } - - subjectPage := NewServerTemplatePage("find_teams_subject", c.TeamUrl) - bodyPage := NewServerTemplatePage("find_teams_body", c.TeamUrl) - - for key, _ := range utils.Cfg.ServiceSettings.Shards { - url := fmt.Sprintf("%v://%v.%v/api/v1", protocol, key, utils.Cfg.ServiceSettings.Domain) - - if strings.Index(utils.Cfg.ServiceSettings.Domain, "localhost") == 0 { - url = fmt.Sprintf("%v://%v/api/v1", protocol, utils.Cfg.ServiceSettings.Domain) - } - - client := model.NewClient(url) - - if result, err := client.FindTeams(email); err != nil { - l4g.Error("An error occured while finding teams at %v err=%v", key, err) - } else { - data := result.Data.([]string) - for _, domain := range data { - bodyPage.Props[fmt.Sprintf("%v://%v.%v", protocol, domain, utils.Cfg.ServiceSettings.Domain)] = "" - } - } - } + subjectPage := NewServerTemplatePage("find_teams_subject", c.GetSiteURL()) + bodyPage := NewServerTemplatePage("find_teams_body", c.GetSiteURL()) if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil { l4g.Error("An error occured while sending an email in emailTeams err=%v", err) @@ -470,22 +330,16 @@ func inviteMembers(c *Context, w http.ResponseWriter, r *http.Request) { ia = append(ia, invite["email"]) } - InviteMembers(team, user, ia) + InviteMembers(c, team, user, ia) w.Write([]byte(invites.ToJson())) } -func InviteMembers(team *model.Team, user *model.User, invites []string) { +func InviteMembers(c *Context, team *model.Team, user *model.User, invites []string) { for _, invite := range invites { if len(invite) > 0 { - teamUrl := "" - if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV { - teamUrl = "http://localhost:8065" - } else if utils.Cfg.ServiceSettings.UseSSL { - teamUrl = fmt.Sprintf("https://%v.%v", team.Domain, utils.Cfg.ServiceSettings.Domain) - } else { - teamUrl = fmt.Sprintf("http://%v.%v", team.Domain, utils.Cfg.ServiceSettings.Domain) - } + teamURL := "" + teamURL = c.GetTeamURLFromTeam(team) sender := user.GetDisplayName() @@ -496,11 +350,11 @@ func InviteMembers(team *model.Team, user *model.User, invites []string) { senderRole = "member" } - subjectPage := NewServerTemplatePage("invite_subject", teamUrl) + subjectPage := NewServerTemplatePage("invite_subject", teamURL) subjectPage.Props["SenderName"] = sender - subjectPage.Props["TeamName"] = team.Name - bodyPage := NewServerTemplatePage("invite_body", teamUrl) - bodyPage.Props["TeamName"] = team.Name + subjectPage.Props["TeamDisplayName"] = team.DisplayName + bodyPage := NewServerTemplatePage("invite_body", teamURL) + bodyPage.Props["TeamDisplayName"] = team.DisplayName bodyPage.Props["SenderName"] = sender bodyPage.Props["SenderStatus"] = senderRole @@ -509,12 +363,12 @@ func InviteMembers(team *model.Team, user *model.User, invites []string) { props := make(map[string]string) props["email"] = invite props["id"] = team.Id + props["display_name"] = team.DisplayName props["name"] = team.Name - props["domain"] = team.Domain props["time"] = fmt.Sprintf("%v", model.GetMillis()) data := model.MapToJson(props) hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt)) - bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&h=%s", teamUrl, url.QueryEscape(data), url.QueryEscape(hash)) + bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&h=%s", c.GetSiteURL(), url.QueryEscape(data), url.QueryEscape(hash)) if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV { l4g.Info("sending invitation to %v %v", invite, bodyPage.Props["Link"]) @@ -527,35 +381,35 @@ func InviteMembers(team *model.Team, user *model.User, invites []string) { } } -func updateTeamName(c *Context, w http.ResponseWriter, r *http.Request) { +func updateTeamDisplayName(c *Context, w http.ResponseWriter, r *http.Request) { props := model.MapFromJson(r.Body) new_name := props["new_name"] if len(new_name) == 0 { - c.SetInvalidParam("updateTeamName", "new_name") + c.SetInvalidParam("updateTeamDisplayName", "new_name") return } teamId := props["team_id"] if len(teamId) > 0 && len(teamId) != 26 { - c.SetInvalidParam("updateTeamName", "team_id") + c.SetInvalidParam("updateTeamDisplayName", "team_id") return } else if len(teamId) == 0 { teamId = c.Session.TeamId } - if !c.HasPermissionsToTeam(teamId, "updateTeamName") { + if !c.HasPermissionsToTeam(teamId, "updateTeamDisplayName") { return } if !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) { - c.Err = model.NewAppError("updateTeamName", "You do not have the appropriate permissions", "userId="+c.Session.UserId) + c.Err = model.NewAppError("updateTeamDisplayName", "You do not have the appropriate permissions", "userId="+c.Session.UserId) c.Err.StatusCode = http.StatusForbidden return } - if result := <-Srv.Store.Team().UpdateName(new_name, c.Session.TeamId); result.Err != nil { + if result := <-Srv.Store.Team().UpdateDisplayName(new_name, c.Session.TeamId); result.Err != nil { c.Err = result.Err return } diff --git a/api/team_test.go b/api/team_test.go index f61babe8e6..2723eff577 100644 --- a/api/team_test.go +++ b/api/team_test.go @@ -32,7 +32,7 @@ func TestCreateFromSignupTeam(t *testing.T) { data := model.MapToJson(props) hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt)) - team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} user := model.User{Email: props["email"], Nickname: "Corey Hulen", Password: "hello"} ts := model.TeamSignup{Team: team, User: user, Invites: []string{"corey@test.com"}, Data: data, Hash: hash} @@ -42,7 +42,7 @@ func TestCreateFromSignupTeam(t *testing.T) { t.Fatal(err) } - if rts.Data.(*model.TeamSignup).Team.Name != team.Name { + if rts.Data.(*model.TeamSignup).Team.DisplayName != team.DisplayName { t.Fatal("full name didn't match") } @@ -71,7 +71,7 @@ func TestCreateFromSignupTeam(t *testing.T) { func TestCreateTeam(t *testing.T) { Setup() - team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, err := Client.CreateTeam(&team) if err != nil { t.Fatal(err) @@ -81,14 +81,14 @@ func TestCreateTeam(t *testing.T) { user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList) if len(c1.Channels) != 2 { t.Fatal("default channels not created") } - if rteam.Data.(*model.Team).Name != team.Name { + if rteam.Data.(*model.Team).DisplayName != team.DisplayName { t.Fatal("full name didn't match") } @@ -111,7 +111,7 @@ func TestCreateTeam(t *testing.T) { func TestFindTeamByEmail(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -122,7 +122,7 @@ func TestFindTeamByEmail(t *testing.T) { t.Fatal(err) } else { domains := r1.Data.([]string) - if domains[0] != team.Domain { + if domains[0] != team.Name { t.Fatal(domains) } } @@ -139,14 +139,14 @@ XXXXXX investigate and fix failing test func TestFindTeamByDomain(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - if r1, err := Client.FindTeamByDomain(team.Domain, false); err != nil { + if r1, err := Client.FindTeamByDomain(team.Name, false); err != nil { t.Fatal(err) } else { val := r1.Data.(bool) @@ -155,7 +155,7 @@ func TestFindTeamByDomain(t *testing.T) { } } - if r1, err := Client.FindTeamByDomain(team.Domain, true); err != nil { + if r1, err := Client.FindTeamByDomain(team.Name, true); err != nil { t.Fatal(err) } else { val := r1.Data.(bool) @@ -179,7 +179,7 @@ func TestFindTeamByDomain(t *testing.T) { func TestFindTeamByEmailSend(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -203,14 +203,14 @@ func TestFindTeamByEmailSend(t *testing.T) { func TestInviteMembers(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") invite := make(map[string]string) invite["email"] = model.NewId() + "corey@test.com" @@ -229,10 +229,10 @@ func TestInviteMembers(t *testing.T) { } } -func TestUpdateTeamName(t *testing.T) { +func TestUpdateTeamDisplayName(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -243,40 +243,40 @@ func TestUpdateTeamName(t *testing.T) { user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - Client.LoginByEmail(team.Domain, user2.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, "pwd") data := make(map[string]string) data["new_name"] = "NewName" - if _, err := Client.UpdateTeamName(data); err == nil { + if _, err := Client.UpdateTeamDisplayName(data); err == nil { t.Fatal("Should have errored, not admin") } - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") data["new_name"] = "" - if _, err := Client.UpdateTeamName(data); err == nil { + if _, err := Client.UpdateTeamDisplayName(data); err == nil { t.Fatal("Should have errored, empty name") } data["new_name"] = "NewName" - if _, err := Client.UpdateTeamName(data); err != nil { + if _, err := Client.UpdateTeamDisplayName(data); err != nil { t.Fatal(err) } // No GET team web service, so hard to confirm here that team name updated data["team_id"] = "junk" - if _, err := Client.UpdateTeamName(data); err == nil { + if _, err := Client.UpdateTeamDisplayName(data); err == nil { t.Fatal("Should have errored, junk team id") } data["team_id"] = "12345678901234567890123456" - if _, err := Client.UpdateTeamName(data); err == nil { + if _, err := Client.UpdateTeamDisplayName(data); err == nil { t.Fatal("Should have errored, bad team id") } data["team_id"] = team.Id data["new_name"] = "NewNameAgain" - if _, err := Client.UpdateTeamName(data); err != nil { + if _, err := Client.UpdateTeamDisplayName(data); err != nil { t.Fatal(err) } // No GET team web service, so hard to confirm here that team name updated @@ -285,17 +285,17 @@ func TestUpdateTeamName(t *testing.T) { func TestFuzzyTeamCreate(t *testing.T) { for i := 0; i < len(utils.FUZZY_STRINGS_NAMES) || i < len(utils.FUZZY_STRINGS_EMAILS); i++ { - testName := "Name" + testDisplayName := "Name" testEmail := "test@nowhere.com" if i < len(utils.FUZZY_STRINGS_NAMES) { - testName = utils.FUZZY_STRINGS_NAMES[i] + testDisplayName = utils.FUZZY_STRINGS_NAMES[i] } if i < len(utils.FUZZY_STRINGS_EMAILS) { testEmail = utils.FUZZY_STRINGS_EMAILS[i] } - team := model.Team{Name: testName, Domain: "z-z-" + model.NewId() + "a", Email: testEmail, Type: model.TEAM_OPEN} + team := model.Team{DisplayName: testDisplayName, Name: "z-z-" + model.NewId() + "a", Email: testEmail, Type: model.TEAM_OPEN} _, err := Client.CreateTeam(&team) if err != nil { @@ -307,22 +307,22 @@ func TestFuzzyTeamCreate(t *testing.T) { func TestGetMyTeam(t *testing.T) { Setup() - team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} ruser, _ := Client.CreateUser(&user, "") store.Must(Srv.Store.User().VerifyEmail(ruser.Data.(*model.User).Id)) - Client.LoginByEmail(team.Domain, user.Email, user.Password) + Client.LoginByEmail(team.Name, user.Email, user.Password) if result, err := Client.GetMyTeam(""); err != nil { t.Fatal("Failed to get user") } else { - if result.Data.(*model.Team).Name != team.Name { + if result.Data.(*model.Team).DisplayName != team.DisplayName { t.Fatal("team names did not match") } - if result.Data.(*model.Team).Domain != team.Domain { + if result.Data.(*model.Team).Name != team.Name { t.Fatal("team domains did not match") } if result.Data.(*model.Team).Type != team.Type { @@ -334,7 +334,7 @@ func TestGetMyTeam(t *testing.T) { func TestUpdateValetFeature(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -345,14 +345,14 @@ func TestUpdateValetFeature(t *testing.T) { user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team2 := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user3.Id)) - Client.LoginByEmail(team.Domain, user2.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, "pwd") data := make(map[string]string) data["allow_valet"] = "true" @@ -360,7 +360,7 @@ func TestUpdateValetFeature(t *testing.T) { t.Fatal("Should have errored, not admin") } - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") data["allow_valet"] = "" if _, err := Client.UpdateValetFeature(data); err == nil { @@ -398,7 +398,7 @@ func TestUpdateValetFeature(t *testing.T) { t.Fatal("Should have errored - allow valet property not updated") } - Client.LoginByEmail(team2.Domain, user3.Email, "pwd") + Client.LoginByEmail(team2.Name, user3.Email, "pwd") data["team_id"] = team.Id data["allow_valet"] = "true" diff --git a/api/templates/email_change_body.html b/api/templates/email_change_body.html index dffe589cd2..f8f3845e78 100644 --- a/api/templates/email_change_body.html +++ b/api/templates/email_change_body.html @@ -9,7 +9,7 @@ @@ -18,7 +18,7 @@ @@ -34,7 +34,7 @@
                  - +

                  You updated your email

                  -

                  You updated your email for {{.Props.TeamName}} on {{ .TeamUrl }}
                  If this change wasn't initiated by you, please reply to this email and let us know.

                  +

                  You updated your email for {{.Props.TeamDisplayName}} on {{ .TeamURL }}
                  If this change wasn't initiated by you, please reply to this email and let us know.

                  - +

                  (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.
                  diff --git a/api/templates/email_change_subject.html b/api/templates/email_change_subject.html index 612dfcbe7c..5690b148a4 100644 --- a/api/templates/email_change_subject.html +++ b/api/templates/email_change_subject.html @@ -1 +1 @@ -{{define "email_change_subject"}}You updated your email for {{.Props.TeamName}} on {{ .Props.Domain }}{{end}} +{{define "email_change_subject"}}You updated your email for {{.Props.TeamDisplayName}} on {{ .Props.Domain }}{{end}} diff --git a/api/templates/find_teams_body.html b/api/templates/find_teams_body.html index d8b582b8a2..6eaaf56e07 100644 --- a/api/templates/find_teams_body.html +++ b/api/templates/find_teams_body.html @@ -9,7 +9,7 @@ @@ -42,7 +42,7 @@
                  - +

                  - +

                  (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.
                  diff --git a/api/templates/invite_body.html b/api/templates/invite_body.html index 8be2ac0df2..46189fae57 100644 --- a/api/templates/invite_body.html +++ b/api/templates/invite_body.html @@ -9,7 +9,7 @@ @@ -18,7 +18,7 @@
                  - +

                  You've been invited

                  -

                  {{.Props.TeamName}} started using {{.SiteName}}.
                  The team {{.Props.SenderStatus}} {{.Props.SenderName}}, has invited you to join {{.Props.TeamName}}.

                  +

                  {{.Props.TeamDisplayName}} started using {{.SiteName}}.
                  The team {{.Props.SenderStatus}} {{.Props.SenderName}}, has invited you to join {{.Props.TeamDisplayName}}.

                  Join Team

                  @@ -37,7 +37,7 @@

                  - +

                  (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.
                  diff --git a/api/templates/invite_subject.html b/api/templates/invite_subject.html index 4be15e3436..6a1e57dcca 100644 --- a/api/templates/invite_subject.html +++ b/api/templates/invite_subject.html @@ -1 +1 @@ -{{define "invite_subject"}}{{ .Props.SenderName }} invited you to join {{ .Props.TeamName }} Team on {{.SiteName}}{{end}} +{{define "invite_subject"}}{{ .Props.SenderName }} invited you to join {{ .Props.TeamDisplayName }} Team on {{.SiteName}}{{end}} diff --git a/api/templates/password_change_body.html b/api/templates/password_change_body.html index f6499d46dc..515c0a7d9d 100644 --- a/api/templates/password_change_body.html +++ b/api/templates/password_change_body.html @@ -9,7 +9,7 @@ @@ -18,7 +18,7 @@ @@ -34,7 +34,7 @@
                  - +

                  You updated your password

                  -

                  You updated your password for {{.Props.TeamName}} on {{ .TeamUrl }} by {{.Props.Method}}.
                  If this change wasn't initiated by you, please reply to this email and let us know.

                  +

                  You updated your password for {{.Props.TeamDisplayName}} on {{ .TeamURL }} by {{.Props.Method}}.
                  If this change wasn't initiated by you, please reply to this email and let us know.

                  - +

                  (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.
                  diff --git a/api/templates/password_change_subject.html b/api/templates/password_change_subject.html index 57422c6925..55daefdb1c 100644 --- a/api/templates/password_change_subject.html +++ b/api/templates/password_change_subject.html @@ -1 +1 @@ -{{define "password_change_subject"}}You updated your password for {{.Props.TeamName}} on {{ .SiteName }}{{end}} +{{define "password_change_subject"}}You updated your password for {{.Props.TeamDisplayName}} on {{ .SiteName }}{{end}} diff --git a/api/templates/post_body.html b/api/templates/post_body.html index 069cdf1fb8..41a29d020d 100644 --- a/api/templates/post_body.html +++ b/api/templates/post_body.html @@ -9,7 +9,7 @@ @@ -37,7 +37,7 @@
                  - +

                  - +

                  (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.
                  diff --git a/api/templates/post_subject.html b/api/templates/post_subject.html index 32df31018c..8ebc9550b9 100644 --- a/api/templates/post_subject.html +++ b/api/templates/post_subject.html @@ -1 +1 @@ -{{define "post_subject"}}[{{.Props.TeamName}} {{.SiteName}}] {{.Props.SubjectText}} for {{.Props.Month}} {{.Props.Day}}, {{.Props.Year}}{{end}} \ No newline at end of file +{{define "post_subject"}}[{{.Props.TeamDisplayName}} {{.SiteName}}] {{.Props.SubjectText}} for {{.Props.Month}} {{.Props.Day}}, {{.Props.Year}}{{end}} diff --git a/api/templates/reset_body.html b/api/templates/reset_body.html index 4b59766637..af9f6b4e8b 100644 --- a/api/templates/reset_body.html +++ b/api/templates/reset_body.html @@ -9,7 +9,7 @@ @@ -37,7 +37,7 @@
                  - +

                  - +

                  (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.
                  diff --git a/api/templates/signup_team_body.html b/api/templates/signup_team_body.html index 6f8bbd92d3..5a5ae4d477 100644 --- a/api/templates/signup_team_body.html +++ b/api/templates/signup_team_body.html @@ -9,7 +9,7 @@ @@ -40,7 +40,7 @@
                  - +

                  - +

                  (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.
                  diff --git a/api/templates/verify_body.html b/api/templates/verify_body.html index 56d85572b1..67ded9c20e 100644 --- a/api/templates/verify_body.html +++ b/api/templates/verify_body.html @@ -9,7 +9,7 @@ @@ -37,7 +37,7 @@
                  - +

                  - +

                  (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.
                  diff --git a/api/templates/verify_subject.html b/api/templates/verify_subject.html index de0ef1d7a5..a661507356 100644 --- a/api/templates/verify_subject.html +++ b/api/templates/verify_subject.html @@ -1 +1 @@ -{{define "verify_subject"}}[{{ .Props.TeamName }} {{ .SiteName }}] Email Verification{{end}} \ No newline at end of file +{{define "verify_subject"}}[{{ .Props.TeamDisplayName }} {{ .SiteName }}] Email Verification{{end}} diff --git a/api/templates/welcome_body.html b/api/templates/welcome_body.html index fbc2e5551e..7107bc2e06 100644 --- a/api/templates/welcome_body.html +++ b/api/templates/welcome_body.html @@ -9,7 +9,7 @@ @@ -17,8 +17,8 @@
                  - +
                  @@ -34,7 +34,7 @@
                  -

                  You joined the {{.Props.TeamName}} team at {{.SiteName}}!

                  -

                  Please let me know if you have any questions.
                  Enjoy your stay at {{.SiteName}}.

                  +

                  You joined the {{.Props.TeamDisplayName}} team at {{.SiteName}}!

                  +

                  Please let me know if you have any questions.
                  Enjoy your stay at {{.SiteName}}.

                  - +

                  (c) 2015 SpinPunch, Inc. 855 El Camino Real, 13A-168, Palo Alto, CA, 94301.
                  diff --git a/api/user.go b/api/user.go index f6422f844a..18c5e863a2 100644 --- a/api/user.go +++ b/api/user.go @@ -181,14 +181,13 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User { l4g.Error("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", ruser.Id, ruser.TeamId, err) } - //fireAndForgetWelcomeEmail(ruser.FirstName, ruser.Email, team.Name, c.TeamUrl+"/channels/town-square") - + //fireAndForgetWelcomeEmail(ruser.FirstName, ruser.Email, team.Name, c.TeamURL+"/channels/town-square") if user.EmailVerified { if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { l4g.Error("Failed to set email verified err=%v", cresult.Err) } } else { - FireAndForgetVerifyEmail(result.Data.(*model.User).Id, ruser.FirstName, ruser.Email, team.Name, c.TeamUrl) + FireAndForgetVerifyEmail(result.Data.(*model.User).Id, ruser.FirstName, ruser.Email, team.DisplayName, c.GetTeamURLFromTeam(team)) } ruser.Sanitize(map[string]bool{}) @@ -202,13 +201,13 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User { } } -func fireAndForgetWelcomeEmail(name, email, teamName, link string) { +func fireAndForgetWelcomeEmail(name, email, teamDisplayName, link string) { go func() { subjectPage := NewServerTemplatePage("welcome_subject", link) bodyPage := NewServerTemplatePage("welcome_body", link) bodyPage.Props["Nickname"] = name - bodyPage.Props["TeamName"] = teamName + bodyPage.Props["TeamDisplayName"] = teamDisplayName bodyPage.Props["FeedbackName"] = utils.Cfg.EmailSettings.FeedbackName if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil { @@ -218,16 +217,16 @@ func fireAndForgetWelcomeEmail(name, email, teamName, link string) { }() } -func FireAndForgetVerifyEmail(userId, name, email, teamName, teamUrl string) { +func FireAndForgetVerifyEmail(userId, name, email, teamDisplayName, teamURL string) { go func() { - link := fmt.Sprintf("%s/verify?uid=%s&hid=%s", teamUrl, userId, model.HashPassword(userId)) + link := fmt.Sprintf("%s/verify_email?uid=%s&hid=%s", teamURL, userId, model.HashPassword(userId)) - subjectPage := NewServerTemplatePage("verify_subject", teamUrl) - subjectPage.Props["TeamName"] = teamName - bodyPage := NewServerTemplatePage("verify_body", teamUrl) + subjectPage := NewServerTemplatePage("verify_subject", teamURL) + subjectPage.Props["TeamDisplayName"] = teamDisplayName + bodyPage := NewServerTemplatePage("verify_body", teamURL) bodyPage.Props["Nickname"] = name - bodyPage.Props["TeamName"] = teamName + bodyPage.Props["TeamDisplayName"] = teamDisplayName bodyPage.Props["VerifyUrl"] = link if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil { @@ -251,10 +250,10 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) { } var team *model.Team - if result.Data == nil && len(props["email"]) != 0 && len(props["domain"]) != 0 { - extraInfo = props["email"] + " in " + props["domain"] + if result.Data == nil && len(props["email"]) != 0 && len(props["name"]) != 0 { + extraInfo = props["email"] + " in " + props["name"] - if nr := <-Srv.Store.Team().GetByDomain(props["domain"]); nr.Err != nil { + if nr := <-Srv.Store.Team().GetByName(props["name"]); nr.Err != nil { c.Err = nr.Err return } else { @@ -517,7 +516,6 @@ func getProfiles(c *Context, w http.ResponseWriter, r *http.Request) { } w.Header().Set(model.HEADER_ETAG_SERVER, etag) - w.Header().Set("Cache-Control", "max-age=120, public") // 2 mins w.Write([]byte(model.UserMapToJson(profiles))) return } @@ -758,7 +756,8 @@ func updateUser(c *Context, w http.ResponseWriter, r *http.Request) { if tresult := <-Srv.Store.Team().Get(rusers[1].TeamId); tresult.Err != nil { l4g.Error(tresult.Err.Message) } else { - fireAndForgetEmailChangeEmail(rusers[1].Email, tresult.Data.(*model.Team).Name, c.TeamUrl) + team := tresult.Data.(*model.Team) + fireAndForgetEmailChangeEmail(rusers[1].Email, team.DisplayName, c.GetTeamURLFromTeam(team)) } } @@ -829,7 +828,8 @@ func updatePassword(c *Context, w http.ResponseWriter, r *http.Request) { if tresult := <-tchan; tresult.Err != nil { l4g.Error(tresult.Err.Message) } else { - fireAndForgetPasswordChangeEmail(user.Email, tresult.Data.(*model.Team).Name, c.TeamUrl, "using the settings menu") + team := tresult.Data.(*model.Team) + fireAndForgetPasswordChangeEmail(user.Email, team.DisplayName, c.GetTeamURLFromTeam(team), "using the settings menu") } data := make(map[string]string) @@ -988,14 +988,14 @@ func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) { return } - domain := props["domain"] - if len(domain) == 0 { - c.SetInvalidParam("sendPasswordReset", "domain") + name := props["name"] + if len(name) == 0 { + c.SetInvalidParam("sendPasswordReset", "name") return } var team *model.Team - if result := <-Srv.Store.Team().GetByDomain(domain); result.Err != nil { + if result := <-Srv.Store.Team().GetByName(name); result.Err != nil { c.Err = result.Err return } else { @@ -1017,10 +1017,10 @@ func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) { data := model.MapToJson(newProps) hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.ResetSalt)) - link := fmt.Sprintf("%s/reset_password?d=%s&h=%s", c.TeamUrl, url.QueryEscape(data), url.QueryEscape(hash)) + link := fmt.Sprintf("%s/reset_password?d=%s&h=%s", c.GetTeamURLFromTeam(team), url.QueryEscape(data), url.QueryEscape(hash)) - subjectPage := NewServerTemplatePage("reset_subject", c.TeamUrl) - bodyPage := NewServerTemplatePage("reset_body", c.TeamUrl) + subjectPage := NewServerTemplatePage("reset_subject", c.GetTeamURLFromTeam(team)) + bodyPage := NewServerTemplatePage("reset_body", c.GetTeamURLFromTeam(team)) bodyPage.Props["ResetUrl"] = link if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil { @@ -1062,16 +1062,16 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) { return } - domain := props["domain"] - if len(domain) == 0 { - c.SetInvalidParam("resetPassword", "domain") + name := props["name"] + if len(name) == 0 { + c.SetInvalidParam("resetPassword", "name") return } c.LogAuditWithUserId(userId, "attempt") var team *model.Team - if result := <-Srv.Store.Team().GetByDomain(domain); result.Err != nil { + if result := <-Srv.Store.Team().GetByName(name); result.Err != nil { c.Err = result.Err return } else { @@ -1110,19 +1110,19 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) { c.LogAuditWithUserId(userId, "success") } - fireAndForgetPasswordChangeEmail(user.Email, team.Name, c.TeamUrl, "using a reset password link") + fireAndForgetPasswordChangeEmail(user.Email, team.DisplayName, c.GetTeamURLFromTeam(team), "using a reset password link") props["new_password"] = "" w.Write([]byte(model.MapToJson(props))) } -func fireAndForgetPasswordChangeEmail(email, teamName, teamUrl, method string) { +func fireAndForgetPasswordChangeEmail(email, teamDisplayName, teamURL, method string) { go func() { - subjectPage := NewServerTemplatePage("password_change_subject", teamUrl) - subjectPage.Props["TeamName"] = teamName - bodyPage := NewServerTemplatePage("password_change_body", teamUrl) - bodyPage.Props["TeamName"] = teamName + subjectPage := NewServerTemplatePage("password_change_subject", teamURL) + subjectPage.Props["TeamDisplayName"] = teamDisplayName + bodyPage := NewServerTemplatePage("password_change_body", teamURL) + bodyPage.Props["TeamDisplayName"] = teamDisplayName bodyPage.Props["Method"] = method if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil { @@ -1132,13 +1132,13 @@ func fireAndForgetPasswordChangeEmail(email, teamName, teamUrl, method string) { }() } -func fireAndForgetEmailChangeEmail(email, teamName, teamUrl string) { +func fireAndForgetEmailChangeEmail(email, teamDisplayName, teamURL string) { go func() { - subjectPage := NewServerTemplatePage("email_change_subject", teamUrl) - subjectPage.Props["TeamName"] = teamName - bodyPage := NewServerTemplatePage("email_change_body", teamUrl) - bodyPage.Props["TeamName"] = teamName + subjectPage := NewServerTemplatePage("email_change_subject", teamURL) + subjectPage.Props["TeamDisplayName"] = teamDisplayName + bodyPage := NewServerTemplatePage("email_change_body", teamURL) + bodyPage.Props["TeamDisplayName"] = teamDisplayName if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil { l4g.Error("Failed to send update password email successfully err=%v", err) diff --git a/api/user_test.go b/api/user_test.go index edbef7c9ab..fbd13492b8 100644 --- a/api/user_test.go +++ b/api/user_test.go @@ -25,7 +25,7 @@ import ( func TestCreateUser(t *testing.T) { Setup() - team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "hello"} @@ -75,7 +75,7 @@ func TestCreateUser(t *testing.T) { func TestCreateUserAllowedDomains(t *testing.T) { Setup() - team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_INVITE, AllowedDomains: "spinpunch.com, @nowh.com,@hello.com"} + team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_INVITE, AllowedDomains: "spinpunch.com, @nowh.com,@hello.com"} rteam, _ := Client.CreateTeam(&team) user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "hello"} @@ -95,7 +95,7 @@ func TestCreateUserAllowedDomains(t *testing.T) { func TestLogin(t *testing.T) { Setup() - team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -110,7 +110,7 @@ func TestLogin(t *testing.T) { } } - if result, err := Client.LoginByEmail(team.Domain, user.Email, user.Password); err != nil { + if result, err := Client.LoginByEmail(team.Name, user.Email, user.Password); err != nil { t.Fatal(err) } else { if result.Data.(*model.User).Email != user.Email { @@ -118,11 +118,11 @@ func TestLogin(t *testing.T) { } } - if _, err := Client.LoginByEmail(team.Domain, user.Email, user.Password+"invalid"); err == nil { + if _, err := Client.LoginByEmail(team.Name, user.Email, user.Password+"invalid"); err == nil { t.Fatal("Invalid Password") } - if _, err := Client.LoginByEmail(team.Domain, "", user.Password); err == nil { + if _, err := Client.LoginByEmail(team.Name, "", user.Password); err == nil { t.Fatal("should have failed") } @@ -135,7 +135,7 @@ func TestLogin(t *testing.T) { Client.AuthToken = "" - team2 := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_INVITE} + team2 := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_INVITE} rteam2 := Client.Must(Client.CreateTeam(&team2)) user2 := model.User{TeamId: rteam2.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -147,14 +147,14 @@ func TestLogin(t *testing.T) { props := make(map[string]string) props["email"] = user2.Email props["id"] = rteam2.Data.(*model.Team).Id - props["name"] = rteam2.Data.(*model.Team).Name + props["display_name"] = rteam2.Data.(*model.Team).DisplayName props["time"] = fmt.Sprintf("%v", model.GetMillis()) data := model.MapToJson(props) hash := model.HashPassword(fmt.Sprintf("%v:%v", data, utils.Cfg.ServiceSettings.InviteSalt)) ruser2, _ := Client.CreateUserFromSignup(&user2, data, hash) - if _, err := Client.LoginByEmail(team2.Domain, ruser2.Data.(*model.User).Email, user2.Password); err != nil { + if _, err := Client.LoginByEmail(team2.Name, ruser2.Data.(*model.User).Email, user2.Password); err != nil { t.Fatal("From verfied hash") } @@ -164,7 +164,7 @@ func TestLogin(t *testing.T) { func TestSessions(t *testing.T) { Setup() - team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -172,8 +172,8 @@ func TestSessions(t *testing.T) { store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) deviceId := model.NewId() - Client.LoginByEmailWithDevice(team.Domain, user.Email, user.Password, deviceId) - Client.LoginByEmail(team.Domain, user.Email, user.Password) + Client.LoginByEmailWithDevice(team.Name, user.Email, user.Password, deviceId) + Client.LoginByEmail(team.Name, user.Email, user.Password) r1, err := Client.GetSessions(ruser.Id) if err != nil { @@ -221,7 +221,7 @@ func TestSessions(t *testing.T) { func TestGetUser(t *testing.T) { Setup() - team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -232,7 +232,7 @@ func TestGetUser(t *testing.T) { ruser2, _ := Client.CreateUser(&user2, "") store.Must(Srv.Store.User().VerifyEmail(ruser2.Data.(*model.User).Id)) - Client.LoginByEmail(team.Domain, user.Email, user.Password) + Client.LoginByEmail(team.Name, user.Email, user.Password) rId := ruser.Data.(*model.User).Id if result, err := Client.GetUser(rId, ""); err != nil { @@ -292,7 +292,7 @@ func TestGetUser(t *testing.T) { func TestGetAudits(t *testing.T) { Setup() - team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -301,7 +301,7 @@ func TestGetAudits(t *testing.T) { time.Sleep(100 * time.Millisecond) - Client.LoginByEmail(team.Domain, user.Email, user.Password) + Client.LoginByEmail(team.Name, user.Email, user.Password) time.Sleep(100 * time.Millisecond) @@ -345,14 +345,14 @@ func TestUserCreateImage(t *testing.T) { t.Fatal("Failed to create correct color") } - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user.Id)) - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") Client.DoGet("/users/"+user.Id+"/image", "", "") @@ -361,7 +361,7 @@ func TestUserCreateImage(t *testing.T) { func TestUserUploadProfileImage(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -377,7 +377,7 @@ func TestUserUploadProfileImage(t *testing.T) { t.Fatal("Should have errored") } - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") if _, upErr := Client.UploadFile("/users/newimage", body.Bytes(), writer.FormDataContentType()); upErr == nil { t.Fatal("Should have errored") @@ -458,7 +458,7 @@ func TestUserUploadProfileImage(t *testing.T) { func TestUserUpdate(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) time1 := model.GetMillis() @@ -471,7 +471,7 @@ func TestUserUpdate(t *testing.T) { t.Fatal("Should have errored") } - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") time.Sleep(100 * time.Millisecond) @@ -523,7 +523,7 @@ func TestUserUpdate(t *testing.T) { user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - Client.LoginByEmail(team.Domain, user2.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, "pwd") user.Nickname = "Tim Timmy" @@ -535,7 +535,7 @@ func TestUserUpdate(t *testing.T) { func TestUserUpdatePassword(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -546,7 +546,7 @@ func TestUserUpdatePassword(t *testing.T) { t.Fatal("Should have errored") } - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") if _, err := Client.UpdateUserPassword("123", "pwd", "newpwd"); err == nil { t.Fatal("Should have errored") @@ -577,14 +577,14 @@ func TestUserUpdatePassword(t *testing.T) { t.Fatal("LastPasswordUpdate should have changed") } - if _, err := Client.LoginByEmail(team.Domain, user.Email, "newpwd"); err != nil { + if _, err := Client.LoginByEmail(team.Name, user.Email, "newpwd"); err != nil { t.Fatal(err) } user2 := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) - Client.LoginByEmail(team.Domain, user2.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, "pwd") if _, err := Client.UpdateUserPassword(user.Id, "pwd", "newpwd"); err == nil { t.Fatal("Should have errored") @@ -594,7 +594,7 @@ func TestUserUpdatePassword(t *testing.T) { func TestUserUpdateRoles(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -613,20 +613,20 @@ func TestUserUpdateRoles(t *testing.T) { t.Fatal("Should have errored, not logged in") } - Client.LoginByEmail(team.Domain, user2.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, "pwd") if _, err := Client.UpdateUserRoles(data); err == nil { t.Fatal("Should have errored, not admin") } - team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team2 := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) user3 := &model.User{TeamId: team2.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user3.Id)) - Client.LoginByEmail(team2.Domain, user3.Email, "pwd") + Client.LoginByEmail(team2.Name, user3.Email, "pwd") data["user_id"] = user2.Id @@ -634,7 +634,7 @@ func TestUserUpdateRoles(t *testing.T) { t.Fatal("Should have errored, wrong team") } - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") data["user_id"] = "junk" data["new_roles"] = "admin" @@ -663,7 +663,7 @@ func TestUserUpdateRoles(t *testing.T) { func TestUserUpdateActive(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -678,26 +678,26 @@ func TestUserUpdateActive(t *testing.T) { t.Fatal("Should have errored, not logged in") } - Client.LoginByEmail(team.Domain, user2.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, "pwd") if _, err := Client.UpdateActive(user.Id, false); err == nil { t.Fatal("Should have errored, not admin") } - team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team2 := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) user3 := &model.User{TeamId: team2.Id, Email: "test@nowhere.com", Nickname: "Corey Hulen", Password: "pwd"} user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user3.Id)) - Client.LoginByEmail(team2.Domain, user3.Email, "pwd") + Client.LoginByEmail(team2.Name, user3.Email, "pwd") if _, err := Client.UpdateActive(user.Id, false); err == nil { t.Fatal("Should have errored, wrong team") } - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") if _, err := Client.UpdateActive("junk", false); err == nil { t.Fatal("Should have errored, bad id") @@ -727,7 +727,7 @@ func TestUserUpdateActive(t *testing.T) { func TestSendPasswordReset(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -736,7 +736,7 @@ func TestSendPasswordReset(t *testing.T) { data := make(map[string]string) data["email"] = user.Email - data["domain"] = team.Domain + data["name"] = team.Name if _, err := Client.SendPasswordReset(data); err != nil { t.Fatal(err) @@ -753,21 +753,21 @@ func TestSendPasswordReset(t *testing.T) { } data["email"] = user.Email - data["domain"] = "" + data["name"] = "" if _, err := Client.SendPasswordReset(data); err == nil { - t.Fatal("Should have errored - no domain") + t.Fatal("Should have errored - no name") } - data["domain"] = "junk" + data["name"] = "junk" if _, err := Client.SendPasswordReset(data); err == nil { - t.Fatal("Should have errored - bad domain") + t.Fatal("Should have errored - bad name") } } func TestResetPassword(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} @@ -781,7 +781,7 @@ func TestResetPassword(t *testing.T) { props["time"] = fmt.Sprintf("%v", model.GetMillis()) data["data"] = model.MapToJson(props) data["hash"] = model.HashPassword(fmt.Sprintf("%v:%v", data["data"], utils.Cfg.ServiceSettings.ResetSalt)) - data["domain"] = team.Domain + data["name"] = team.Name if _, err := Client.ResetPassword(data); err != nil { t.Fatal(err) @@ -839,10 +839,10 @@ func TestResetPassword(t *testing.T) { t.Fatal("Should have errored - bad domain") } - team2 := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test2@nowhere.com", Type: model.TEAM_OPEN} + team2 := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test2@nowhere.com", Type: model.TEAM_OPEN} team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team) - data["domain"] = team2.Domain + data["domain"] = team2.Name if _, err := Client.ResetPassword(data); err == nil { t.Fatal("Should have errored - domain team doesn't match user team") } @@ -851,7 +851,7 @@ func TestResetPassword(t *testing.T) { func TestUserUpdateNotify(t *testing.T) { Setup() - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user := &model.User{TeamId: team.Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd", Roles: ""} @@ -868,7 +868,7 @@ func TestUserUpdateNotify(t *testing.T) { t.Fatal("Should have errored - not logged in") } - Client.LoginByEmail(team.Domain, user.Email, "pwd") + Client.LoginByEmail(team.Name, user.Email, "pwd") if result, err := Client.UpdateUserNotify(data); err != nil { t.Fatal(err) @@ -919,7 +919,7 @@ func TestUserUpdateNotify(t *testing.T) { func TestFuzzyUserCreate(t *testing.T) { - team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) for i := 0; i < len(utils.FUZZY_STRINGS_NAMES) || i < len(utils.FUZZY_STRINGS_EMAILS); i++ { @@ -945,14 +945,14 @@ func TestFuzzyUserCreate(t *testing.T) { func TestStatuses(t *testing.T) { Setup() - team := model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) user := model.User{TeamId: rteam.Data.(*model.Team).Id, Email: strings.ToLower(model.NewId()) + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} ruser := Client.Must(Client.CreateUser(&user, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(ruser.Id)) - Client.LoginByEmail(team.Domain, user.Email, user.Password) + Client.LoginByEmail(team.Name, user.Email, user.Password) r1, err := Client.GetStatuses() if err != nil { diff --git a/api/web_socket_test.go b/api/web_socket_test.go index 07fdfea361..161274ff79 100644 --- a/api/web_socket_test.go +++ b/api/web_socket_test.go @@ -17,13 +17,13 @@ func TestSocket(t *testing.T) { Setup() url := "ws://localhost:" + utils.Cfg.ServiceSettings.Port + "/api/v1/websocket" - team := &model.Team{Name: "Name", Domain: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} + team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) - Client.LoginByEmail(team.Domain, user1.Email, "pwd") + Client.LoginByEmail(team.Name, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "Test Web Scoket 1", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) @@ -42,7 +42,7 @@ func TestSocket(t *testing.T) { user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) - Client.LoginByEmail(team.Domain, user2.Email, "pwd") + Client.LoginByEmail(team.Name, user2.Email, "pwd") header2 := http.Header{} header2.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken) diff --git a/config/config.json b/config/config.json index 27ca57e865..e6025ef517 100644 --- a/config/config.json +++ b/config/config.json @@ -9,7 +9,6 @@ }, "ServiceSettings": { "SiteName": "Mattermost", - "Domain": "xxxxxxmustbefilledin.com", "Mode" : "dev", "AllowTesting" : false, "UseSSL": false, @@ -35,11 +34,7 @@ "S3AccessKeyId": "", "S3SecretAccessKey": "", "S3Bucket": "", - "S3Region": "", - "Route53AccessKeyId": "", - "Route53SecretAccessKey": "", - "Route53ZoneId": "", - "Route53Region": "" + "S3Region": "" }, "ImageSettings": { "ThumbnailWidth": 200, diff --git a/config/config_docker.json b/config/config_docker.json index 185e9f215e..9be8370724 100644 --- a/config/config_docker.json +++ b/config/config_docker.json @@ -9,7 +9,6 @@ }, "ServiceSettings": { "SiteName": "Mattermost", - "Domain": "", "Mode" : "dev", "AllowTesting" : false, "UseSSL": false, @@ -35,11 +34,7 @@ "S3AccessKeyId": "", "S3SecretAccessKey": "", "S3Bucket": "", - "S3Region": "", - "Route53AccessKeyId": "", - "Route53SecretAccessKey": "", - "Route53ZoneId": "", - "Route53Region": "" + "S3Region": "" }, "ImageSettings": { "ThumbnailWidth": 200, diff --git a/manualtesting/manual_testing.go b/manualtesting/manual_testing.go index 6c4958a7bb..f7408b814a 100644 --- a/manualtesting/manual_testing.go +++ b/manualtesting/manual_testing.go @@ -57,17 +57,17 @@ func manualTest(c *api.Context, w http.ResponseWriter, r *http.Request) { // Check for username parameter and create a user if present username, ok1 := params["username"] - teamname, ok2 := params["teamname"] + teamDisplayName, ok2 := params["teamname"] var teamID string var userID string if ok1 && ok2 { l4g.Info("Creating user and team") // Create team for testing team := &model.Team{ - Name: teamname[0], - Domain: utils.RandomName(utils.Range{20, 20}, utils.LOWERCASE), - Email: utils.RandomEmail(utils.Range{20, 20}, utils.LOWERCASE), - Type: model.TEAM_OPEN, + DisplayName: teamDisplayName[0], + Name: utils.RandomName(utils.Range{20, 20}, utils.LOWERCASE), + Email: utils.RandomEmail(utils.Range{20, 20}, utils.LOWERCASE), + Type: model.TEAM_OPEN, } if result := <-api.Srv.Store.Team().Save(team); result.Err != nil { diff --git a/model/client.go b/model/client.go index ab01e7d62f..c7e17a6db7 100644 --- a/model/client.go +++ b/model/client.go @@ -98,10 +98,10 @@ func (c *Client) Must(result *Result, err *AppError) *Result { return result } -func (c *Client) SignupTeam(email string, name string) (*Result, *AppError) { +func (c *Client) SignupTeam(email string, displayName string) (*Result, *AppError) { m := make(map[string]string) m["email"] = email - m["name"] = name + m["display_name"] = displayName if r, err := c.DoPost("/teams/signup", MapToJson(m)); err != nil { return nil, err } else { @@ -128,11 +128,11 @@ func (c *Client) CreateTeam(team *Team) (*Result, *AppError) { } } -func (c *Client) FindTeamByDomain(domain string, allServers bool) (*Result, *AppError) { +func (c *Client) FindTeamByName(name string, allServers bool) (*Result, *AppError) { m := make(map[string]string) - m["domain"] = domain + m["name"] = name m["all"] = fmt.Sprintf("%v", allServers) - if r, err := c.DoPost("/teams/find_team_by_domain", MapToJson(m)); err != nil { + if r, err := c.DoPost("/teams/find_team_by_name", MapToJson(m)); err != nil { return nil, err } else { val := false @@ -177,7 +177,7 @@ func (c *Client) InviteMembers(invites *Invites) (*Result, *AppError) { } } -func (c *Client) UpdateTeamName(data map[string]string) (*Result, *AppError) { +func (c *Client) UpdateTeamDisplayName(data map[string]string) (*Result, *AppError) { if r, err := c.DoPost("/teams/update_name", MapToJson(data)); err != nil { return nil, err } else { @@ -247,17 +247,17 @@ func (c *Client) LoginById(id string, password string) (*Result, *AppError) { return c.login(m) } -func (c *Client) LoginByEmail(domain string, email string, password string) (*Result, *AppError) { +func (c *Client) LoginByEmail(name string, email string, password string) (*Result, *AppError) { m := make(map[string]string) - m["domain"] = domain + m["name"] = name m["email"] = email m["password"] = password return c.login(m) } -func (c *Client) LoginByEmailWithDevice(domain string, email string, password string, deviceId string) (*Result, *AppError) { +func (c *Client) LoginByEmailWithDevice(name string, email string, password string, deviceId string) (*Result, *AppError) { m := make(map[string]string) - m["domain"] = domain + m["name"] = name m["email"] = email m["password"] = password m["device_id"] = deviceId diff --git a/model/team.go b/model/team.go index 5c66f3b1fb..e7005625b1 100644 --- a/model/team.go +++ b/model/team.go @@ -18,8 +18,8 @@ type Team struct { CreateAt int64 `json:"create_at"` UpdateAt int64 `json:"update_at"` DeleteAt int64 `json:"delete_at"` + DisplayName string `json:"display_name"` Name string `json:"name"` - Domain string `json:"domain"` Email string `json:"email"` Type string `json:"type"` CompanyName string `json:"company_name"` @@ -97,20 +97,20 @@ func (o *Team) IsValid() *AppError { return NewAppError("Team.IsValid", "Invalid email", "id="+o.Id) } - if len(o.Name) > 64 { + if len(o.DisplayName) > 64 { return NewAppError("Team.IsValid", "Invalid name", "id="+o.Id) } - if len(o.Domain) > 64 { - return NewAppError("Team.IsValid", "Invalid domain", "id="+o.Id) + if len(o.Name) > 64 { + return NewAppError("Team.IsValid", "Invalid URL Identifier", "id="+o.Id) } - if IsReservedDomain(o.Domain) { + if IsReservedTeamName(o.Name) { return NewAppError("Team.IsValid", "This URL is unavailable. Please try another.", "id="+o.Id) } - if !IsValidDomain(o.Domain) { - return NewAppError("Team.IsValid", "Domain must be 4 or more lowercase alphanumeric characters", "id="+o.Id) + if !IsValidTeamName(o.Name) { + return NewAppError("Team.IsValid", "Name must be 4 or more lowercase alphanumeric characters", "id="+o.Id) } if !(o.Type == TEAM_OPEN || o.Type == TEAM_INVITE) { diff --git a/model/team_signup_test.go b/model/team_signup_test.go index f3f74470b2..eb2fbc69ff 100644 --- a/model/team_signup_test.go +++ b/model/team_signup_test.go @@ -9,7 +9,7 @@ import ( ) func TestTeamSignupJson(t *testing.T) { - team := Team{Id: NewId(), Name: NewId()} + team := Team{Id: NewId(), DisplayName: NewId()} o := TeamSignup{Team: team, Data: "data"} json := o.ToJson() ro := TeamSignupFromJson(strings.NewReader(json)) diff --git a/model/team_test.go b/model/team_test.go index 6261ed6bf7..071b1a2e9b 100644 --- a/model/team_test.go +++ b/model/team_test.go @@ -9,7 +9,7 @@ import ( ) func TestTeamJson(t *testing.T) { - o := Team{Id: NewId(), Name: NewId()} + o := Team{Id: NewId(), DisplayName: NewId()} json := o.ToJson() ro := TeamFromJson(strings.NewReader(json)) @@ -46,18 +46,18 @@ func TestTeamIsValid(t *testing.T) { } o.Email = "corey@hulen.com" - o.Name = strings.Repeat("01234567890", 20) + o.DisplayName = strings.Repeat("01234567890", 20) if err := o.IsValid(); err == nil { t.Fatal("should be invalid") } - o.Name = "1234" - o.Domain = "ZZZZZZZ" + o.DisplayName = "1234" + o.Name = "ZZZZZZZ" if err := o.IsValid(); err == nil { t.Fatal("should be invalid") } - o.Domain = "zzzzz" + o.Name = "zzzzz" o.Type = TEAM_OPEN if err := o.IsValid(); err != nil { t.Fatal(err) @@ -65,12 +65,12 @@ func TestTeamIsValid(t *testing.T) { } func TestTeamPreSave(t *testing.T) { - o := Team{Name: "test"} + o := Team{DisplayName: "test"} o.PreSave() o.Etag() } func TestTeamPreUpdate(t *testing.T) { - o := Team{Name: "test"} + o := Team{DisplayName: "test"} o.PreUpdate() } diff --git a/model/utils.go b/model/utils.go index 465901a095..38592b984b 100644 --- a/model/utils.go +++ b/model/utils.go @@ -148,7 +148,7 @@ func IsValidEmail(email string) bool { return false } -var reservedDomains = []string{ +var reservedName = []string{ "www", "web", "admin", @@ -168,10 +168,10 @@ var reservedDomains = []string{ "api", } -func IsReservedDomain(s string) bool { +func IsReservedTeamName(s string) bool { s = strings.ToLower(s) - for _, value := range reservedDomains { + for _, value := range reservedName { if strings.Index(s, value) == 0 { return true } @@ -180,7 +180,7 @@ func IsReservedDomain(s string) bool { return false } -func IsValidDomain(s string) bool { +func IsValidTeamName(s string) bool { if !IsValidAlphaNum(s) { return false diff --git a/model/utils_test.go b/model/utils_test.go index a9721042d8..dbb4488823 100644 --- a/model/utils_test.go +++ b/model/utils_test.go @@ -83,9 +83,9 @@ var domains = []struct { {"test", true}, } -func TestValidDomain(t *testing.T) { +func TestValidTeamName(t *testing.T) { for _, v := range domains { - if IsValidDomain(v.value) != v.expected { + if IsValidTeamName(v.value) != v.expected { t.Errorf("expect %v as %v", v.value, v.expected) } } @@ -102,9 +102,9 @@ var tReservedDomains = []struct { {"spin-punch-admin", false}, } -func TestReservedDomain(t *testing.T) { +func TestReservedTeamName(t *testing.T) { for _, v := range tReservedDomains { - if IsReservedDomain(v.value) != v.expected { + if IsReservedTeamName(v.value) != v.expected { t.Errorf("expect %v as %v", v.value, v.expected) } } diff --git a/store/sql_team_store.go b/store/sql_team_store.go index ffb9f80938..b89dca03e0 100644 --- a/store/sql_team_store.go +++ b/store/sql_team_store.go @@ -19,8 +19,8 @@ func NewSqlTeamStore(sqlStore *SqlStore) TeamStore { for _, db := range sqlStore.GetAllConns() { table := db.AddTableWithName(model.Team{}, "Teams").SetKeys(false, "Id") table.ColMap("Id").SetMaxSize(26) - table.ColMap("Name").SetMaxSize(64) - table.ColMap("Domain").SetMaxSize(64).SetUnique(true) + table.ColMap("DisplayName").SetMaxSize(64) + table.ColMap("Name").SetMaxSize(64).SetUnique(true) table.ColMap("Email").SetMaxSize(128) table.ColMap("CompanyName").SetMaxSize(64) table.ColMap("AllowedDomains").SetMaxSize(500) @@ -35,6 +35,10 @@ func (s SqlTeamStore) UpgradeSchemaIfNeeded() { defaultValue = "1" } s.CreateColumnIfNotExists("Teams", "AllowValet", "AllowedDomains", "tinyint(1)", defaultValue) + if !s.DoesColumnExist("Teams", "DisplayName") { + s.RenameColumnIfExists("Teams", "Name", "DisplayName", "varchar(64)") + s.RenameColumnIfExists("Teams", "Domain", "Name", "varchar(64)") + } } func (s SqlTeamStore) CreateIndexesIfNotExists() { @@ -62,7 +66,7 @@ func (s SqlTeamStore) Save(team *model.Team) StoreChannel { } if err := s.GetMaster().Insert(team); err != nil { - if strings.Contains(err.Error(), "Duplicate entry") && strings.Contains(err.Error(), "for key 'Domain'") { + if strings.Contains(err.Error(), "Duplicate entry") && strings.Contains(err.Error(), "for key 'Name'") { result.Err = model.NewAppError("SqlTeamStore.Save", "A team with that domain already exists", "id="+team.Id+", "+err.Error()) } else { result.Err = model.NewAppError("SqlTeamStore.Save", "We couldn't save the team", "id="+team.Id+", "+err.Error()) @@ -100,7 +104,7 @@ func (s SqlTeamStore) Update(team *model.Team) StoreChannel { } else { oldTeam := oldResult.(*model.Team) team.CreateAt = oldTeam.CreateAt - team.Domain = oldTeam.Domain + team.Name = oldTeam.Name if count, err := s.GetMaster().Update(team); err != nil { result.Err = model.NewAppError("SqlTeamStore.Update", "We encounted an error updating the team", "id="+team.Id+", "+err.Error()) @@ -118,15 +122,15 @@ func (s SqlTeamStore) Update(team *model.Team) StoreChannel { return storeChannel } -func (s SqlTeamStore) UpdateName(name string, teamId string) StoreChannel { +func (s SqlTeamStore) UpdateDisplayName(name string, teamId string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} - if _, err := s.GetMaster().Exec("UPDATE Teams SET Name = ? WHERE Id = ?", name, teamId); err != nil { - result.Err = model.NewAppError("SqlTeamStore.UpdateName", "We couldn't update the team name", "team_id="+teamId) + if _, err := s.GetMaster().Exec("UPDATE Teams SET DisplayName = ? WHERE Id = ?", name, teamId); err != nil { + result.Err = model.NewAppError("SqlTeamStore.UpdateDisplayName", "We couldn't update the team name", "team_id="+teamId) } else { result.Data = teamId } @@ -159,7 +163,7 @@ func (s SqlTeamStore) Get(id string) StoreChannel { return storeChannel } -func (s SqlTeamStore) GetByDomain(domain string) StoreChannel { +func (s SqlTeamStore) GetByName(name string) StoreChannel { storeChannel := make(StoreChannel) go func() { @@ -167,8 +171,8 @@ func (s SqlTeamStore) GetByDomain(domain string) StoreChannel { team := model.Team{} - if err := s.GetReplica().SelectOne(&team, "SELECT * FROM Teams WHERE Domain=?", domain); err != nil { - result.Err = model.NewAppError("SqlTeamStore.GetByDomain", "We couldn't find the existing team", "domain="+domain+", "+err.Error()) + if err := s.GetReplica().SelectOne(&team, "SELECT * FROM Teams WHERE Name=?", name); err != nil { + result.Err = model.NewAppError("SqlTeamStore.GetByName", "We couldn't find the existing team", "name="+name+", "+err.Error()) } result.Data = &team diff --git a/store/sql_team_store_test.go b/store/sql_team_store_test.go index bd1a7de2ea..1f13e466cb 100644 --- a/store/sql_team_store_test.go +++ b/store/sql_team_store_test.go @@ -13,8 +13,8 @@ func TestTeamStoreSave(t *testing.T) { Setup() o1 := model.Team{} - o1.Name = "Name" - o1.Domain = "a" + model.NewId() + "b" + o1.DisplayName = "DisplayName" + o1.Name = "a" + model.NewId() + "b" o1.Email = model.NewId() + "@nowhere.com" o1.Type = model.TEAM_OPEN @@ -36,8 +36,8 @@ func TestTeamStoreUpdate(t *testing.T) { Setup() o1 := model.Team{} - o1.Name = "Name" - o1.Domain = "a" + model.NewId() + "b" + o1.DisplayName = "DisplayName" + o1.Name = "a" + model.NewId() + "b" o1.Email = model.NewId() + "@nowhere.com" o1.Type = model.TEAM_OPEN if err := (<-store.Team().Save(&o1)).Err; err != nil { @@ -61,25 +61,25 @@ func TestTeamStoreUpdate(t *testing.T) { } } -func TestTeamStoreUpdateName(t *testing.T) { +func TestTeamStoreUpdateDisplayName(t *testing.T) { Setup() o1 := &model.Team{} - o1.Name = "Name" - o1.Domain = "a" + model.NewId() + "b" + o1.DisplayName = "Display Name" + o1.Name = "a" + model.NewId() + "b" o1.Email = model.NewId() + "@nowhere.com" o1.Type = model.TEAM_OPEN o1 = (<-store.Team().Save(o1)).Data.(*model.Team) - newName := "NewName" + newDisplayName := "NewDisplayName" - if err := (<-store.Team().UpdateName(newName, o1.Id)).Err; err != nil { + if err := (<-store.Team().UpdateDisplayName(newDisplayName, o1.Id)).Err; err != nil { t.Fatal(err) } ro1 := (<-store.Team().Get(o1.Id)).Data.(*model.Team) - if ro1.Name != newName { - t.Fatal("Name not updated") + if ro1.DisplayName != newDisplayName { + t.Fatal("DisplayName not updated") } } @@ -87,8 +87,8 @@ func TestTeamStoreGet(t *testing.T) { Setup() o1 := model.Team{} - o1.Name = "Name" - o1.Domain = "a" + model.NewId() + "b" + o1.DisplayName = "DisplayName" + o1.Name = "a" + model.NewId() + "b" o1.Email = model.NewId() + "@nowhere.com" o1.Type = model.TEAM_OPEN Must(store.Team().Save(&o1)) @@ -106,12 +106,12 @@ func TestTeamStoreGet(t *testing.T) { } } -func TestTeamStoreGetByDomain(t *testing.T) { +func TestTeamStoreGetByName(t *testing.T) { Setup() o1 := model.Team{} - o1.Name = "Name" - o1.Domain = "a" + model.NewId() + "b" + o1.DisplayName = "DisplayName" + o1.Name = "a" + model.NewId() + "b" o1.Email = model.NewId() + "@nowhere.com" o1.Type = model.TEAM_OPEN @@ -119,7 +119,7 @@ func TestTeamStoreGetByDomain(t *testing.T) { t.Fatal(err) } - if r1 := <-store.Team().GetByDomain(o1.Domain); r1.Err != nil { + if r1 := <-store.Team().GetByName(o1.Name); r1.Err != nil { t.Fatal(r1.Err) } else { if r1.Data.(*model.Team).ToJson() != o1.ToJson() { @@ -127,7 +127,7 @@ func TestTeamStoreGetByDomain(t *testing.T) { } } - if err := (<-store.Team().GetByDomain("")).Err; err == nil { + if err := (<-store.Team().GetByName("")).Err; err == nil { t.Fatal("Missing id should have failed") } } @@ -136,8 +136,8 @@ func TestTeamStoreGetForEmail(t *testing.T) { Setup() o1 := model.Team{} - o1.Name = "Name" - o1.Domain = "a" + model.NewId() + "b" + o1.DisplayName = "DisplayName" + o1.Name = "a" + model.NewId() + "b" o1.Email = model.NewId() + "@nowhere.com" o1.Type = model.TEAM_OPEN Must(store.Team().Save(&o1)) diff --git a/store/store.go b/store/store.go index 9faa6a9d74..5b0e13fce6 100644 --- a/store/store.go +++ b/store/store.go @@ -36,9 +36,9 @@ type Store interface { type TeamStore interface { Save(team *model.Team) StoreChannel Update(team *model.Team) StoreChannel - UpdateName(name string, teamId string) StoreChannel + UpdateDisplayName(name string, teamId string) StoreChannel Get(id string) StoreChannel - GetByDomain(domain string) StoreChannel + GetByName(name string) StoreChannel GetTeamsForEmail(domain string) StoreChannel } diff --git a/utils/config.go b/utils/config.go index 6ad29ab7d7..efa4b263a9 100644 --- a/utils/config.go +++ b/utils/config.go @@ -19,13 +19,11 @@ const ( type ServiceSettings struct { SiteName string - Domain string Mode string AllowTesting bool UseSSL bool Port string Version string - Shards map[string]string InviteSalt string PublicLinkSalt string ResetSalt string @@ -52,14 +50,10 @@ type LogSettings struct { } type AWSSettings struct { - S3AccessKeyId string - S3SecretAccessKey string - S3Bucket string - S3Region string - Route53AccessKeyId string - Route53SecretAccessKey string - Route53ZoneId string - Route53Region string + S3AccessKeyId string + S3SecretAccessKey string + S3Bucket string + S3Region string } type ImageSettings struct { @@ -213,18 +207,10 @@ func LoadConfig(fileName string) { panic("Error decoding configuration " + err.Error()) } - // Grabs the domain from enviroment variable if not in configuration - if config.ServiceSettings.Domain == "" { - config.ServiceSettings.Domain = os.Getenv("MATTERMOST_DOMAIN") - // If the enviroment variable is not set, use a default - if config.ServiceSettings.Domain == "" { - config.ServiceSettings.Domain = "localhost" - } - } - // Check for a valid email for feedback, if not then do feedback@domain if _, err := mail.ParseAddress(config.EmailSettings.FeedbackEmail); err != nil { - config.EmailSettings.FeedbackEmail = "feedback@" + config.ServiceSettings.Domain + config.EmailSettings.FeedbackEmail = "feedback@localhost" + l4g.Error("Misconfigured feedback email setting: %s", config.EmailSettings.FeedbackEmail) } configureLog(config.LogSettings) diff --git a/utils/config_test.go b/utils/config_test.go index 9067dc6478..4d37b4e88f 100644 --- a/utils/config_test.go +++ b/utils/config_test.go @@ -4,24 +4,9 @@ package utils import ( - "os" "testing" ) func TestConfig(t *testing.T) { LoadConfig("config.json") } - -func TestEnvOverride(t *testing.T) { - os.Setenv("MATTERMOST_DOMAIN", "testdomain.com") - - LoadConfig("config_docker.json") - if Cfg.ServiceSettings.Domain != "testdomain.com" { - t.Fail() - } - - LoadConfig("config.json") - if Cfg.ServiceSettings.Domain == "testdomain.com" { - t.Fail() - } -} diff --git a/web/react/components/delete_channel_modal.jsx b/web/react/components/delete_channel_modal.jsx index a8c6907893..e23a377402 100644 --- a/web/react/components/delete_channel_modal.jsx +++ b/web/react/components/delete_channel_modal.jsx @@ -12,7 +12,7 @@ module.exports = React.createClass({ Client.deleteChannel(this.state.channel_id, function(data) { AsyncClient.getChannels(true); - window.location.href = '/channels/town-square'; + window.location.href = '/'; }.bind(this), function(err) { AsyncClient.dispatchError(err, "handleDelete"); diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx index 74c7d40659..71fefff5b7 100644 --- a/web/react/components/login.jsx +++ b/web/react/components/login.jsx @@ -1,102 +1,21 @@ // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. - - - var utils = require('../utils/utils.jsx'); var client = require('../utils/client.jsx'); var UserStore = require('../stores/user_store.jsx'); +var TeamStore = require('../stores/team_store.jsx'); var BrowserStore = require('../stores/browser_store.jsx'); - - -var FindTeamDomain = React.createClass({ - handleSubmit: function(e) { - e.preventDefault(); - var state = { } - - var domain = this.refs.domain.getDOMNode().value.trim(); - if (!domain) { - state.server_error = "A domain is required" - this.setState(state); - return; - } - - if (!BrowserStore.isLocalStorageSupported()) { - state.server_error = "This service requires local storage to be enabled. Please enable it or exit private browsing."; - this.setState(state); - return; - } - - state.server_error = ""; - this.setState(state); - - client.findTeamByDomain(domain, - function(data) { - console.log(data); - if (data) { - window.location.href = window.location.protocol + "//" + domain + "." + utils.getDomainWithOutSub(); - } - else { - this.state.server_error = "We couldn't find your " + strings.Team + "."; - this.setState(this.state); - } - }.bind(this), - function(err) { - this.state.server_error = err.message; - this.setState(this.state); - }.bind(this) - ); - }, - getInitialState: function() { - return { }; - }, - render: function() { - var server_error = this.state.server_error ? : null; - - return ( -

                  -
                  - { config.SiteName } -
                  - Enter your {strings.Team}'s domain. -
                  -
                  -
                  - -
                  - { server_error } - -
                  -
                  - -
                  -
                  - Don't remember your {strings.Team}'s domain? Find it here -
                  -
                  -
                  -
                  -
                  -
                  -
                  -
                  - {"Want to create your own " + strings.Team + "?"} Sign up now -
                  - -
                  - ); - } -}); +var Constants = require('../utils/constants.jsx'); module.exports = React.createClass({ handleSubmit: function(e) { e.preventDefault(); var state = { } - var domain = this.refs.domain.getDOMNode().value.trim(); - if (!domain) { - state.server_error = "A domain is required" + var name = this.props.teamName + if (!name) { + state.server_error = "Bad team name" this.setState(state); return; } @@ -124,23 +43,22 @@ module.exports = React.createClass({ state.server_error = ""; this.setState(state); - client.loginByEmail(domain, email, password, + client.loginByEmail(name, email, password, function(data) { - UserStore.setLastDomain(domain); - UserStore.setLastEmail(email); UserStore.setCurrentUser(data); + UserStore.setLastEmail(email); var redirect = utils.getUrlParameter("redirect"); if (redirect) { - window.location.href = decodeURI(redirect); + window.location.pathname = decodeURI(redirect); } else { - window.location.href = '/channels/town-square'; + window.location.pathname = '/' + name + '/channels/town-square'; } }.bind(this), function(err) { if (err.message == "Login failed because email address has not been verified") { - window.location.href = '/verify?domain=' + encodeURIComponent(domain) + '&email=' + encodeURIComponent(email); + window.location.href = '/verify_email?name=' + encodeURIComponent(name) + '&email=' + encodeURIComponent(email); return; } state.server_error = err.message; @@ -161,35 +79,35 @@ module.exports = React.createClass({ priorEmail = decodeURIComponent(emailParam); } - var subDomainClass = "form-control hidden"; - var subDomain = utils.getSubDomain(); + var teamDisplayName = this.props.teamDisplayName; + var teamName = this.props.teamName; - if (utils.isTestDomain()) { - subDomainClass = "form-control"; - subDomain = UserStore.getLastDomain(); - } else if (subDomain == "") { - return (); + var focusEmail = false; + var focusPassword = false; + if (priorEmail != "") { + focusPassword = true; + } else { + focusEmail = true; } return (
                  - { subDomain } + { teamDisplayName }
                  - { utils.getDomainWithOutSub() } + /{ teamName }/

                  { server_error } -
                  - +
                  - +
                  @@ -198,10 +116,10 @@ module.exports = React.createClass({ {"Find other " + strings.TeamPlural}
                  - {"Want to create your own " + strings.Team + "?"} Sign up now + {"Want to create your own " + strings.Team + "?"} Sign up now
                  diff --git a/web/react/components/msg_typing.jsx b/web/react/components/msg_typing.jsx index a6953028f1..aacb315dd9 100644 --- a/web/react/components/msg_typing.jsx +++ b/web/react/components/msg_typing.jsx @@ -51,7 +51,7 @@ module.exports = React.createClass({ }, render: function() { return ( - { this.state.text } + { this.state.text } ); } }); diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx index 78cf7d8b82..34c65c34fd 100644 --- a/web/react/components/navbar.jsx +++ b/web/react/components/navbar.jsx @@ -7,6 +7,7 @@ var client = require('../utils/client.jsx'); var AsyncClient = require('../utils/async_client.jsx'); var UserStore = require('../stores/user_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx'); +var TeamStore = require('../stores/team_store.jsx'); var UserProfile = require('./user_profile.jsx'); var MessageWrapper = require('./message_wrapper.jsx'); @@ -61,93 +62,6 @@ var NotifyCounts = React.createClass({ } }); -var NavbarLoginForm = React.createClass({ - handleSubmit: function(e) { - e.preventDefault(); - var state = { }; - - var domain = this.refs.domain.getDOMNode().value.trim(); - if (!domain) { - state.server_error = "A domain is required"; - this.setState(state); - return; - } - - var email = this.refs.email.getDOMNode().value.trim(); - if (!email) { - state.server_error = "An email is required"; - this.setState(state); - return; - } - - var password = this.refs.password.getDOMNode().value.trim(); - if (!password) { - state.server_error = "A password is required"; - this.setState(state); - return; - } - - state.server_error = ""; - this.setState(state); - - client.loginByEmail(domain, email, password, - function(data) { - UserStore.setLastDomain(domain); - UserStore.setLastEmail(email); - UserStore.setCurrentUser(data); - - var redirect = utils.getUrlParameter("redirect"); - if (redirect) { - window.location.href = decodeURI(redirect); - } else { - window.location.href = '/channels/town-square'; - } - - }, - function(err) { - if (err.message == "Login failed because email address has not been verified") { - window.location.href = '/verify?domain=' + encodeURIComponent(domain) + '&email=' + encodeURIComponent(email); - return; - } - state.server_error = err.message; - this.valid = false; - this.setState(state); - }.bind(this) - ); - }, - getInitialState: function() { - return { }; - }, - render: function() { - var server_error = this.state.server_error ? : null; - - var subDomain = utils.getSubDomain(); - var subDomainClass = "form-control hidden"; - - if (subDomain == "") { - subDomain = UserStore.getLastDomain(); - subDomainClass = "form-control"; - } - - return ( -
                  - Find your team -
                  - { server_error } - -
                  -
                  - -
                  -
                  - -
                  - -
                  - ); - } -}); - function getStateFromStores() { return { channel: ChannelStore.getCurrent(), @@ -180,10 +94,10 @@ module.exports = React.createClass({ }, handleLeave: function(e) { client.leaveChannel(this.state.channel.id, - function() { + function(data, text, req) { AsyncClient.getChannels(true); - window.location.href = '/channels/town-square'; - }, + window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/town-square'; + }.bind(this), function(err) { AsyncClient.dispatchError(err, "handleLeave"); } @@ -229,7 +143,7 @@ module.exports = React.createClass({ var currentId = UserStore.getCurrentId(); var popoverContent = ""; - var channelTitle = this.props.teamName; + var channelTitle = this.props.teamDisplayName; var isAdmin = false; var isDirect = false; var description = "" @@ -333,14 +247,8 @@ module.exports = React.createClass({ - : null } + : "" } - { !currentId ? - - : null - } ); diff --git a/web/react/components/new_channel.jsx b/web/react/components/new_channel.jsx index 069e0d6b1c..49e0884580 100644 --- a/web/react/components/new_channel.jsx +++ b/web/react/components/new_channel.jsx @@ -6,6 +6,8 @@ var utils = require('../utils/utils.jsx'); var client = require('../utils/client.jsx'); var asyncClient = require('../utils/async_client.jsx'); var UserStore = require('../stores/user_store.jsx'); +var TeamStore = require('../stores/team_store.jsx'); +var Constants = require('../utils/constants.jsx'); module.exports = React.createClass({ handleSubmit: function(e) { @@ -60,13 +62,13 @@ module.exports = React.createClass({ var self = this; client.createChannel(channel, - function(data) { + function() { this.refs.display_name.getDOMNode().value = ""; this.refs.channel_name.getDOMNode().value = ""; this.refs.channel_desc.getDOMNode().value = ""; $(self.refs.modal.getDOMNode()).modal('hide'); - window.location.href = "/channels/" + channel.name; + window.location = TeamStore.getCurrentTeamUrl() + "/channels/" + channel.name; asyncClient.getChannels(true); }.bind(this), function(err) { diff --git a/web/react/components/password_reset.jsx b/web/react/components/password_reset.jsx index 24566c7b19..b2edea6208 100644 --- a/web/react/components/password_reset.jsx +++ b/web/react/components/password_reset.jsx @@ -10,13 +10,6 @@ SendResetPasswordLink = React.createClass({ e.preventDefault(); var state = {}; - var domain = this.refs.domain.getDOMNode().value.trim(); - if (!domain) { - state.error = "A domain is required" - this.setState(state); - return; - } - var email = this.refs.email.getDOMNode().value.trim(); if (!email) { state.error = "Please enter a valid email address." @@ -29,17 +22,17 @@ SendResetPasswordLink = React.createClass({ data = {}; data['email'] = email; - data['domain'] = domain; + data['name'] = this.props.teamName; client.sendPasswordReset(data, - function(data) { - this.setState({ error: null, update_text:

                  A password reset link has been sent to {email} for your {this.props.teamName} team on {config.SiteName}.com.

                  , more_update_text: "Please check your inbox." }); - $(this.refs.reset_form.getDOMNode()).hide(); - }.bind(this), - function(err) { - this.setState({ error: err.message, update_text: null, more_update_text: null }); - }.bind(this) - ); + function(data) { + this.setState({ error: null, update_text:

                  A password reset link has been sent to {email} for your {this.props.teamDisplayName} team on {window.location.hostname}.

                  , more_update_text: "Please check your inbox." }); + $(this.refs.reset_form.getDOMNode()).hide(); + }.bind(this), + function(err) { + this.setState({ error: err.message, update_text: null, more_update_text: null }); + }.bind(this) + ); }, getInitialState: function() { return {}; @@ -48,24 +41,13 @@ SendResetPasswordLink = React.createClass({ var update_text = this.state.update_text ?
                  {this.state.update_text}{this.state.more_update_text}
                  : null; var error = this.state.error ?
                  : null; - var subDomain = utils.getSubDomain(); - var subDomainClass = "form-control hidden"; - - if (subDomain == "") { - subDomain = UserStore.getLastDomain(); - subDomainClass = "form-control"; - } - return (

                  Password Reset

                  { update_text }
                  -

                  {"To reset your password, enter the email address you used to sign up for " + this.props.teamName + "."}

                  -
                  - -
                  +

                  {"To reset your password, enter the email address you used to sign up for " + this.props.teamDisplayName + "."}

                  @@ -83,13 +65,6 @@ ResetPassword = React.createClass({ e.preventDefault(); var state = {}; - var domain = this.refs.domain.getDOMNode().value.trim(); - if (!domain) { - state.error = "A domain is required" - this.setState(state); - return; - } - var password = this.refs.password.getDOMNode().value.trim(); if (!password || password.length < 5) { state.error = "Please enter at least 5 characters." @@ -104,41 +79,30 @@ ResetPassword = React.createClass({ data['new_password'] = password; data['hash'] = this.props.hash; data['data'] = this.props.data; - data['domain'] = domain; + data['name'] = this.props.teamName; client.resetPassword(data, - function(data) { - this.setState({ error: null, update_text: "Your password has been updated successfully." }); - }.bind(this), - function(err) { - this.setState({ error: err.message, update_text: null }); - }.bind(this) - ); + function(data) { + this.setState({ error: null, update_text: "Your password has been updated successfully." }); + }.bind(this), + function(err) { + this.setState({ error: err.message, update_text: null }); + }.bind(this) + ); }, getInitialState: function() { return {}; }, render: function() { - var update_text = this.state.update_text ?

                  : null; + var update_text = this.state.update_text ?

                  : null; var error = this.state.error ?
                  : null; - var subDomain = this.props.domain != "" ? this.props.domain : utils.getSubDomain(); - var subDomainClass = "form-control hidden"; - - if (subDomain == "") { - subDomain = UserStore.getLastDomain(); - subDomainClass = "form-control"; - } - return (

                  Password Reset

                  -

                  {"Enter a new password for your " + this.props.teamName + " " + config.SiteName + " account."}

                  -
                  - -
                  +

                  {"Enter a new password for your " + this.props.teamDisplayName + " " + config.SiteName + " account."}

                  @@ -161,14 +125,15 @@ module.exports = React.createClass({ if (this.props.isReset === "false") { return ( ); } else { return ( diff --git a/web/react/components/rename_channel_modal.jsx b/web/react/components/rename_channel_modal.jsx index b4ccb2937a..2ae331626a 100644 --- a/web/react/components/rename_channel_modal.jsx +++ b/web/react/components/rename_channel_modal.jsx @@ -6,6 +6,8 @@ var utils = require('../utils/utils.jsx'); var Client = require('../utils/client.jsx'); var AsyncClient = require('../utils/async_client.jsx'); var ChannelStore = require('../stores/channel_store.jsx'); +var TeamStore = require('../stores/team_store.jsx'); +var Constants = require('../utils/constants.jsx'); module.exports = React.createClass({ handleSubmit: function(e) { @@ -60,12 +62,12 @@ module.exports = React.createClass({ return; Client.updateChannel(channel, - function(data) { + function(data, text, req) { this.refs.display_name.getDOMNode().value = ""; this.refs.channel_name.getDOMNode().value = ""; $('#' + this.props.modalId).modal('hide'); - window.location.href = '/channels/' + this.state.channel_name; + window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + this.state.channel_name; AsyncClient.getChannels(true); }.bind(this), function(err) { diff --git a/web/react/components/rename_team_modal.jsx b/web/react/components/rename_team_modal.jsx index 67a150b9d3..a6da57b67f 100644 --- a/web/react/components/rename_team_modal.jsx +++ b/web/react/components/rename_team_modal.jsx @@ -24,13 +24,13 @@ module.exports = React.createClass({ if (!valid) return; - if (this.props.teamName === name) + if (this.props.teamDisplayName === name) return; var data = {}; data["new_name"] = name; - Client.updateTeamName(data, + Client.updateTeamDisplayName(data, function(data) { $('#rename_team_link').modal('hide'); window.location.reload(); @@ -47,11 +47,11 @@ module.exports = React.createClass({ componentDidMount: function() { var self = this; $(this.refs.modal.getDOMNode()).on('hidden.bs.modal', function(e) { - self.setState({ name: self.props.teamName }); + self.setState({ name: self.props.teamDisplayName }); }); }, getInitialState: function() { - return { name: this.props.teamName }; + return { name: this.props.teamDisplayName }; }, render: function() { diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index 65727c5972..3cf67e410e 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -6,6 +6,7 @@ var ChannelStore = require('../stores/channel_store.jsx'); var AsyncClient = require('../utils/async_client.jsx'); var SocketStore = require('../stores/socket_store.jsx'); var UserStore = require('../stores/user_store.jsx'); +var TeamStore = require('../stores/team_store.jsx'); var utils = require('../utils/utils.jsx'); var SidebarHeader = require('./sidebar_header.jsx'); var SearchBox = require('./search_bar.jsx'); @@ -13,93 +14,6 @@ var SearchBox = require('./search_bar.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; -var SidebarLoginForm = React.createClass({ - handleSubmit: function(e) { - e.preventDefault(); - var state = { } - - var domain = this.refs.domain.getDOMNode().value.trim(); - if (!domain) { - state.server_error = "A domain is required" - this.setState(state); - return; - } - - var email = this.refs.email.getDOMNode().value.trim(); - if (!email) { - state.server_error = "An email is required" - this.setState(state); - return; - } - - var password = this.refs.password.getDOMNode().value.trim(); - if (!password) { - state.server_error = "A password is required" - this.setState(state); - return; - } - - state.server_error = ""; - this.setState(state); - - client.loginByEmail(domain, email, password, - function(data) { - UserStore.setLastDomain(domain); - UserStore.setLastEmail(email); - UserStore.setCurrentUser(data); - - var redirect = utils.getUrlParameter("redirect"); - if (redirect) { - window.location.href = decodeURI(redirect); - } else { - window.location.href = '/channels/town-square'; - } - - }.bind(this), - function(err) { - if (err.message == "Login failed because email address has not been verified") { - window.location.href = '/verify?domain=' + encodeURIComponent(domain) + '&email=' + encodeURIComponent(email); - return; - } - state.server_error = err.message; - this.valid = false; - this.setState(state); - }.bind(this) - ); - }, - getInitialState: function() { - return { }; - }, - render: function() { - var server_error = this.state.server_error ? : null; - - var subDomain = utils.getSubDomain(); - var subDomainClass = "form-control hidden"; - - if (subDomain == "") { - subDomain = UserStore.getLastDomain(); - subDomainClass = "form-control"; - } - - return ( - - {"Find your " + strings.Team} -
                  - { server_error } - -
                  -
                  - -
                  -
                  - -
                  - - - ); - } -}); - function getStateFromStores() { var members = ChannelStore.getAllMembers(); var team_member_map = UserStore.getActiveOnlyProfiles(); @@ -192,7 +106,7 @@ function getStateFromStores() { }; } -var SidebarLoggedIn = React.createClass({ +module.exports = React.createClass({ componentDidMount: function() { ChannelStore.addChangeListener(this._onChange); UserStore.addChangeListener(this._onChange); @@ -383,7 +297,7 @@ var SidebarLoggedIn = React.createClass({ ); } else { return ( -
                • {badge}{channel.display_name}
                • +
                • {badge}{channel.display_name}
                • ); } @@ -414,7 +328,7 @@ var SidebarLoggedIn = React.createClass({ } return (
                  - +
                  @@ -440,25 +354,3 @@ var SidebarLoggedIn = React.createClass({ ); } }); - -var SidebarLoggedOut = React.createClass({ - render: function() { - return ( -
                  - - -
                  - ); - } -}); - -module.exports = React.createClass({ - render: function() { - var currentId = UserStore.getCurrentId(); - if (currentId != null) { - return ; - } else { - return ; - } - } -}); diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx index 45c9ca629b..bab2897b6b 100644 --- a/web/react/components/sidebar_header.jsx +++ b/web/react/components/sidebar_header.jsx @@ -61,19 +61,15 @@ var NavbarDropdown = React.createClass({ var teams = []; + teams.push(
                • ); if (this.state.teams.length > 1) { for (var i = 0; i < this.state.teams.length; i++) { - var domain = this.state.teams[i]; + var teamName = this.state.teams[i]; - if (domain == utils.getSubDomain()) - continue; - - if (teams.length == 0) - teams.push(
                • ); - - teams.push(
                • Switch to { domain }
                • ); + teams.push(
                • Switch to { teamName }
                • ); } } + teams.push(
                • Create a New Team
                • ); return (
                    @@ -110,19 +106,21 @@ module.exports = React.createClass({ }, render: function() { - var me = UserStore.getCurrentUser(); - + var teamDisplayName = this.props.teamDisplayName ? this.props.teamDisplayName : config.SiteName; + var me = UserStore.getCurrentUser() if (!me) { return null; } return (
                    - -
                    {'@' + me.username}
                    - {this.props.teamName} -
                    + + +
                    { '@' + me.username}
                    +
                    { teamDisplayName }
                    +
                  +
                  ); diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx index 22d1d9ad29..15306a499c 100644 --- a/web/react/components/sidebar_right_menu.jsx +++ b/web/react/components/sidebar_right_menu.jsx @@ -49,12 +49,12 @@ module.exports = React.createClass({ } var siteName = config.SiteName != null ? config.SiteName : ""; - var teamName = this.props.teamName ? this.props.teamName : siteName; + var teamDisplayName = this.props.teamDisplayName ? this.props.teamDisplayName : siteName; return (
                  diff --git a/web/react/components/signup_team.jsx b/web/react/components/signup_team.jsx index 22086250c0..cf982cc1eb 100644 --- a/web/react/components/signup_team.jsx +++ b/web/react/components/signup_team.jsx @@ -20,8 +20,8 @@ module.exports = React.createClass({ state.email_error = ""; } - team.name = this.refs.name.getDOMNode().value.trim(); - if (!team.name) { + team.display_name = this.refs.name.getDOMNode().value.trim(); + if (!team.display_name) { state.name_error = "This field is required"; state.inValid = true; } @@ -34,7 +34,7 @@ module.exports = React.createClass({ return; } - client.signupTeam(team.email, team.name, + client.signupTeam(team.email, team.display_name, function(data) { if (data["follow_link"]) { window.location.href = data["follow_link"]; @@ -61,7 +61,7 @@ module.exports = React.createClass({ return (
                  - + { email_error }
                  @@ -70,6 +70,9 @@ module.exports = React.createClass({
                  { server_error } +
                  ); } diff --git a/web/react/components/signup_team_complete.jsx b/web/react/components/signup_team_complete.jsx index 9e2a139554..9ceeb63240 100644 --- a/web/react/components/signup_team_complete.jsx +++ b/web/react/components/signup_team_complete.jsx @@ -15,7 +15,7 @@ WelcomePage = React.createClass({ return; } e.preventDefault(); - this.props.state.wizard = "team_name"; + this.props.state.wizard = "team_display_name"; this.props.updateParent(this.props.state); }, handleDiffEmail: function (e) { @@ -57,6 +57,17 @@ WelcomePage = React.createClass({ getInitialState: function() { return { use_diff: false }; }, + handleKeyPress: function(event) { + if (event.keyCode == 13) { + this.submitNext(event); + } + }, + componentWillMount: function() { + document.addEventListener("keyup", this.handleKeyPress, false); + }, + componentWillUnmount: function() { + document.removeEventListener("keyup", this.handleKeyPress, false); + }, render: function() { client.track('signup', 'signup_team_01_welcome'); @@ -77,7 +88,7 @@ WelcomePage = React.createClass({ { this.props.state.team.email }

                  - + { storage_error }

                  @@ -92,15 +103,15 @@ WelcomePage = React.createClass({ { email_error }
                  { server_error } - +
                  - +
                  ); } }); -TeamNamePage = React.createClass({ +TeamDisplayNamePage = React.createClass({ submitBack: function (e) { e.preventDefault(); this.props.state.wizard = "welcome"; @@ -109,19 +120,24 @@ TeamNamePage = React.createClass({ submitNext: function (e) { e.preventDefault(); - var name = this.refs.name.getDOMNode().value.trim(); - if (!name) { + var display_name = this.refs.name.getDOMNode().value.trim(); + if (!display_name) { this.setState({name_error: "This field is required"}); return; } this.props.state.wizard = "team_url"; - this.props.state.team.name = name; + this.props.state.team.display_name = display_name; this.props.updateParent(this.props.state); }, getInitialState: function() { return { }; }, + handleFocus: function(e) { + e.preventDefault(); + + e.currentTarget.select(); + }, render: function() { client.track('signup', 'signup_team_02_name'); @@ -130,29 +146,31 @@ TeamNamePage = React.createClass({ return (
                  +

                  {utils.toTitleCase(strings.Team) + " Name"}

                  - +
                  { name_error }

                  {"Your " + strings.Team + " name shows in menus and headings. It may include the name of your " + strings.Company + ", but it's not required."}

                  -   - +   + +
                  ); } }); -TeamUrlPage = React.createClass({ +TeamURLPage = React.createClass({ submitBack: function (e) { e.preventDefault(); - this.props.state.wizard = "team_name"; + this.props.state.wizard = "team_display_name"; this.props.updateParent(this.props.state); }, submitNext: function (e) { @@ -172,18 +190,18 @@ TeamUrlPage = React.createClass({ return; } else if (cleaned_name.length <= 3 || cleaned_name.length > 15) { - this.setState({name_error: "Domain must be 4 or more characters up to a maximum of 15"}) + this.setState({name_error: "Name must be 4 or more characters up to a maximum of 15"}) return; } - for (var index = 0; index < constants.RESERVED_DOMAINS.length; index++) { - if (cleaned_name.indexOf(constants.RESERVED_DOMAINS[index]) == 0) { - this.setState({name_error: "This Team URL name is unavailable"}) + for (var index = 0; index < constants.RESERVED_TEAM_NAMES.length; index++) { + if (cleaned_name.indexOf(constants.RESERVED_TEAM_NAMES[index]) == 0) { + this.setState({name_error: "This team name is unavailable"}) return; } } - client.findTeamByDomain(name, + client.findTeamByName(name, function(data) { if (!data) { if (config.AllowSignupDomainsWizard) { @@ -193,7 +211,7 @@ TeamUrlPage = React.createClass({ this.props.state.team.type = 'O'; } - this.props.state.team.domain = name; + this.props.state.team.name = name; this.props.updateParent(this.props.state); } else { @@ -210,6 +228,11 @@ TeamUrlPage = React.createClass({ getInitialState: function() { return { }; }, + handleFocus: function(e) { + e.preventDefault(); + + e.currentTarget.select(); + }, render: function() { client.track('signup', 'signup_team_03_url'); @@ -218,14 +241,15 @@ TeamUrlPage = React.createClass({ return (
                  +

                  {utils.toTitleCase(strings.Team) + " URL"}

                  - - .{ utils.getDomainWithOutSub() } + { window.location.origin + "/" } +
                  @@ -233,8 +257,9 @@ TeamUrlPage = React.createClass({

                  {"Pick something short and memorable for your " + strings.Team + "'s web address."}

                  {"Your " + strings.Team + " URL can only contain lowercase letters, numbers and dashes. Also, it needs to start with a letter and cannot end in a dash."}

                  -   - +   + +
                  ); } @@ -291,6 +316,7 @@ AllowedDomainsPage = React.createClass({ return (
                  +

                  Email Domain

                  @@ -303,7 +329,7 @@ AllowedDomainsPage = React.createClass({

                  @ - +
                  @@ -313,8 +339,9 @@ AllowedDomainsPage = React.createClass({

                  -   - +   + +
                  ); } @@ -356,7 +383,7 @@ EmailItem = React.createClass({ return (
                  - + { email_error }
                  ); @@ -424,16 +451,22 @@ SendInivtesPage = React.createClass({ var emails = []; for (var i = 0; i < this.props.state.invites.length; i++) { - emails.push(); + if (i == 0) { + emails.push(); + } else { + emails.push(); + } } return (
                  +

                  Send Invitations

                  { emails } -
                  -
                   
                  +
                  +
                   
                  +

                  {"If you'd prefer, you can send invitations after you finish setting up the "+ strings.Team + "."}

                  @@ -477,20 +510,22 @@ UsernamePage = React.createClass({ return (
                  +

                  Choose a username

                  - +
                  { name_error }

                  {"Pick something " + strings.Team + "mates will recognize. Your username is how you will appear to others."}

                  It can be made of lowercase letters and numbers.

                  -   - +   + +
                  ); } @@ -531,18 +566,11 @@ PasswordPage = React.createClass({ props.state.wizard = "finished"; props.updateParent(props.state, true); - if (utils.isTestDomain()) { - UserStore.setLastDomain(teamSignup.team.domain); - UserStore.setLastEmail(teamSignup.team.email); - window.location.href = window.location.protocol + '//' + utils.getDomainWithOutSub() + '/login?email=' + encodeURIComponent(teamSignup.team.email); - } - else { - window.location.href = window.location.protocol + '//' + teamSignup.team.domain + '.' + utils.getDomainWithOutSub() + '/login?email=' + encodeURIComponent(teamSignup.team.email); - } + window.location.href = window.location.origin + '/' + props.state.team.name + '/login?email=' + encodeURIComponent(teamSignup.team.email); // client.loginByEmail(teamSignup.team.domain, teamSignup.team.email, teamSignup.user.password, // function(data) { - // UserStore.setLastDomain(teamSignup.team.domain); + // TeamStore.setLastName(teamSignup.team.domain); // UserStore.setLastEmail(teamSignup.team.email); // UserStore.setCurrentUser(data); // window.location.href = '/channels/town-square'; @@ -570,13 +598,14 @@ PasswordPage = React.createClass({ return (
                  +

                  Choose a password

                  You'll use your email address ({this.props.state.team.email}) and password to log into {config.SiteName}.

                  - +
                  { name_error } @@ -585,10 +614,11 @@ PasswordPage = React.createClass({
                  -   - +   +

                  By proceeding to create your account and use { config.SiteName }, you agree to our Terms of Service and Privacy Policy. If you do not agree, you cannot use {config.SiteName}.

                  +
                  ); } @@ -610,9 +640,9 @@ module.exports = React.createClass({ props.wizard = "welcome"; props.team = {}; props.team.email = this.props.email; - props.team.name = this.props.name; + props.team.display_name = this.props.name; props.team.company_name = this.props.name; - props.team.domain = utils.cleanUpUrlable(this.props.name); + props.team.name = utils.cleanUpUrlable(this.props.name); props.team.allowed_domains = ""; props.invites = []; props.invites.push(""); @@ -630,12 +660,12 @@ module.exports = React.createClass({ return } - if (this.state.wizard == "team_name") { - return + if (this.state.wizard == "team_display_name") { + return } if (this.state.wizard == "team_url") { - return + return } if (this.state.wizard == "allowed_domains") { diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx index d1053c7783..eed323d1f5 100644 --- a/web/react/components/signup_user_complete.jsx +++ b/web/react/components/signup_user_complete.jsx @@ -48,16 +48,17 @@ module.exports = React.createClass({ client.loginByEmail(this.props.domain, this.state.user.email, this.state.user.password, function(data) { - UserStore.setLastDomain(this.props.domain); UserStore.setLastEmail(this.state.user.email); UserStore.setCurrentUser(data); if (this.props.hash > 0) + { BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: "finished"})); - window.location.href = '/channels/town-square'; + } + window.location.href = '/'; }.bind(this), function(err) { if (err.message == "Login failed because email address has not been verified") { - window.location.href = "/verify?email="+ encodeURIComponent(this.state.user.email) + "&domain=" + encodeURIComponent(this.props.domain); + window.location.href = "/verify_email?email="+ encodeURIComponent(this.state.user.email) + "&domain=" + encodeURIComponent(this.props.domain); } else { this.state.server_error = err.message; this.setState(this.state); diff --git a/web/react/components/team_members.jsx b/web/react/components/team_members.jsx index 6b978f88bc..616fd2c995 100644 --- a/web/react/components/team_members.jsx +++ b/web/react/components/team_members.jsx @@ -57,7 +57,7 @@ module.exports = React.createClass({
                  -

                  {this.props.teamName + " Members"}

                  +

                  {this.props.teamDisplayName + " Members"}

                  diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx index 3aa9858637..f70d60e3af 100644 --- a/web/react/pages/channel.jsx +++ b/web/react/pages/channel.jsx @@ -61,17 +61,17 @@ global.window.setup_channel_page = function(team_name, team_type, team_id, chann ); React.render( - , + , document.getElementById('navbar') ); React.render( - , + , document.getElementById('sidebar-left') ); React.render( - , + , document.getElementById('rename_team_modal') ); @@ -91,7 +91,7 @@ global.window.setup_channel_page = function(team_name, team_type, team_id, chann ); React.render( - , + , document.getElementById('team_members_modal') ); @@ -186,7 +186,7 @@ global.window.setup_channel_page = function(team_name, team_type, team_id, chann ); React.render( - , + , document.getElementById('sidebar-menu') ); diff --git a/web/react/pages/home.jsx b/web/react/pages/home.jsx index 08dd32f73a..b12fa49491 100644 --- a/web/react/pages/home.jsx +++ b/web/react/pages/home.jsx @@ -2,13 +2,14 @@ // See License.txt for license information. var ChannelStore = require('../stores/channel_store.jsx'); +var TeamStore = require('../stores/team_store.jsx'); var Constants = require('../utils/constants.jsx'); -global.window.setup_home_page = function() { +global.window.setup_home_page = function(teamURL) { var last = ChannelStore.getLastVisitedName(); if (last == null || last.length === 0) { - window.location.replace("/channels/" + Constants.DEFAULT_CHANNEL); + window.location = teamURL + "/channels/" + Constants.DEFAULT_CHANNEL; } else { - window.location.replace("/channels/" + last); + window.location = teamURL + "/channels/" + last; } } diff --git a/web/react/pages/login.jsx b/web/react/pages/login.jsx index a4e6b438eb..8348f0b5d3 100644 --- a/web/react/pages/login.jsx +++ b/web/react/pages/login.jsx @@ -3,9 +3,9 @@ var Login = require('../components/login.jsx'); -global.window.setup_login_page = function() { +global.window.setup_login_page = function(teamDisplayName, teamName) { React.render( - , + , document.getElementById('login') ); }; diff --git a/web/react/pages/password_reset.jsx b/web/react/pages/password_reset.jsx index 6d0d88a10e..c7a208973a 100644 --- a/web/react/pages/password_reset.jsx +++ b/web/react/pages/password_reset.jsx @@ -3,13 +3,13 @@ var PasswordReset = require('../components/password_reset.jsx'); -global.window.setup_password_reset_page = function(is_reset, team_name, domain, hash, data) { +global.window.setup_password_reset_page = function(is_reset, team_display_name, team_name, hash, data) { React.render( , diff --git a/web/react/pages/signup_team_complete.jsx b/web/react/pages/signup_team_complete.jsx index c17cbdfacc..346f2ab5ab 100644 --- a/web/react/pages/signup_team_complete.jsx +++ b/web/react/pages/signup_team_complete.jsx @@ -5,7 +5,7 @@ var SignupTeamComplete =require('../components/signup_team_complete.jsx'); global.window.setup_signup_team_complete_page = function(email, name, data, hash) { React.render( - , + , document.getElementById('signup-team-complete') ); -}; \ No newline at end of file +}; diff --git a/web/react/stores/browser_store.jsx b/web/react/stores/browser_store.jsx index 4d6eb0b8db..4eed754cc9 100644 --- a/web/react/stores/browser_store.jsx +++ b/web/react/stores/browser_store.jsx @@ -8,7 +8,7 @@ function getPrefix() { } // Also change model/utils.go ETAG_ROOT_VERSION -var BROWSER_STORE_VERSION = '.3'; +var BROWSER_STORE_VERSION = '.4'; module.exports = { _initialized: false, diff --git a/web/react/stores/post_store.jsx b/web/react/stores/post_store.jsx index e773bb6889..5280bfe08c 100644 --- a/web/react/stores/post_store.jsx +++ b/web/react/stores/post_store.jsx @@ -151,12 +151,12 @@ var PostStore = assign({}, EventEmitter.prototype, { return BrowserStore.getItem("draft_" + channel_id + "_" + user_id); }, clearDraftUploads: function() { - BrowserStore.actionOnItemsWithPrefix("draft_", function (key, value) { - if (value) { - value.uploadsInProgress = 0; - BrowserStore.setItem(key, value); - } - }); + BrowserStore.actionOnItemsWithPrefix("draft_", function (key, value) { + if (value) { + value.uploadsInProgress = 0; + BrowserStore.setItem(key, value); + } + }); } }); diff --git a/web/react/stores/team_store.jsx b/web/react/stores/team_store.jsx index b7199a4a8f..3f12725f81 100644 --- a/web/react/stores/team_store.jsx +++ b/web/react/stores/team_store.jsx @@ -57,10 +57,13 @@ var TeamStore = assign({}, EventEmitter.prototype, { else return null; }, + getCurrentTeamUrl: function() { + return window.location.origin + "/" + this.getCurrent().name; + }, storeTeam: function(team) { - var teams = this._getTeams(); - teams[team.id] = team; - this._storeTeams(teams); + var teams = this._getTeams(); + teams[team.id] = team; + this._storeTeams(teams); }, _storeTeams: function(teams) { BrowserStore.setItem("user_teams", teams); diff --git a/web/react/stores/user_store.jsx b/web/react/stores/user_store.jsx index b0ea719d48..d03016c5d6 100644 --- a/web/react/stores/user_store.jsx +++ b/web/react/stores/user_store.jsx @@ -67,9 +67,18 @@ var UserStore = assign({}, EventEmitter.prototype, { }, setCurrentId: function(id) { this._current_id = id; + if (id == null) { + BrowserStore.removeGlobalItem("current_user_id"); + } else { + BrowserStore.setGlobalItem("current_user_id", id); + } }, getCurrentId: function(skipFetch) { - var current_id = this._current_id; + var current_id = this._current_id; + + if (current_id == null) { + current_id = BrowserStore.getGlobalItem("current_user_id"); + } // this is a speical case to force fetch the // current user if it's missing @@ -92,17 +101,11 @@ var UserStore = assign({}, EventEmitter.prototype, { return this._getProfiles()[this.getCurrentId()]; }, setCurrentUser: function(user) { - this.saveProfile(user); this.setCurrentId(user.id); - }, - getLastDomain: function() { - return BrowserStore.getItem("last_domain", ''); - }, - setLastDomain: function(domain) { - BrowserStore.setItem("last_domain", domain); + this.saveProfile(user); }, getLastEmail: function() { - return BrowserStore.getItem("last_email", ''); + return BrowserStore.getItem("last_email", ''); }, setLastEmail: function(email) { BrowserStore.setItem("last_email", email); @@ -144,18 +147,18 @@ var UserStore = assign({}, EventEmitter.prototype, { this._storeProfiles(ps); }, _storeProfiles: function(profiles) { - BrowserStore.setGlobalItem("profiles", profiles); + BrowserStore.setItem("profiles", profiles); var profileUsernameMap = {}; for (var id in profiles) { profileUsernameMap[profiles[id].username] = profiles[id]; } - BrowserStore.setGlobalItem("profileUsernameMap", profileUsernameMap); + BrowserStore.setItem("profileUsernameMap", profileUsernameMap); }, _getProfiles: function() { - return BrowserStore.getGlobalItem("profiles", {}); + return BrowserStore.getItem("profiles", {}); }, _getProfilesUsernameMap: function() { - return BrowserStore.getGlobalItem("profileUsernameMap", {}); + return BrowserStore.getItem("profileUsernameMap", {}); }, setSessions: function(sessions) { BrowserStore.setItem("sessions", sessions); diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx index 11d4c26013..1c31dc5ed7 100644 --- a/web/react/utils/client.jsx +++ b/web/react/utils/client.jsx @@ -2,6 +2,7 @@ // See License.txt for license information. var BrowserStore = require('../stores/browser_store.jsx'); +var TeamStore = require('../stores/team_store.jsx'); module.exports.track = function(category, action, label, prop, val) { global.window.snowplow('trackStructEvent', category, action, label, prop, val); @@ -44,7 +45,12 @@ function handleError(method_name, xhr, status, err) { module.exports.track('api', 'api_weberror', method_name, 'message', msg); if (xhr.status == 401) { - window.location.href = '/login?redirect=' + encodeURIComponent(window.location.pathname+window.location.search); + if (window.location.href.indexOf("/channels") === 0) { + window.location.pathname = '/login?redirect=' + encodeURIComponent(window.location.pathname+window.location.search); + } else { + var teamURL = window.location.href.split('/channels')[0]; + window.location.href = teamURL + '/login?redirect=' + encodeURIComponent(window.location.pathname+window.location.search); + } } return e; @@ -205,17 +211,18 @@ module.exports.resetPassword = function(data, success, error) { module.exports.logout = function() { module.exports.track('api', 'api_users_logout'); - BrowserStore.clear(); - window.location.href = "/logout"; + var currentTeamUrl = TeamStore.getCurrentTeamUrl(); + BrowserStore.clear(); + window.location.href = currentTeamUrl + "/logout"; }; -module.exports.loginByEmail = function(domain, email, password, success, error) { +module.exports.loginByEmail = function(name, email, password, success, error) { $.ajax({ url: "/api/v1/users/login", dataType: 'json', contentType: 'application/json', type: 'POST', - data: JSON.stringify({domain: domain, email: email, password: password}), + data: JSON.stringify({name: name, email: email, password: password}), success: function(data, textStatus, xhr) { module.exports.track('api', 'api_users_login_success', data.team_id, 'email', data.email); success(data, textStatus, xhr); @@ -317,7 +324,7 @@ module.exports.inviteMembers = function(data, success, error) { module.exports.track('api', 'api_teams_invite_members'); }; -module.exports.updateTeamName = function(data, success, error) { +module.exports.updateTeamDisplayName = function(data, success, error) { $.ajax({ url: "/api/v1/teams/update_name", dataType: 'json', @@ -326,7 +333,7 @@ module.exports.updateTeamName = function(data, success, error) { data: JSON.stringify(data), success: success, error: function(xhr, status, err) { - e = handleError("updateTeamName", xhr, status, err); + e = handleError("updateTeamDisplayName", xhr, status, err); error(e); } }); @@ -334,13 +341,13 @@ module.exports.updateTeamName = function(data, success, error) { module.exports.track('api', 'api_teams_update_name'); }; -module.exports.signupTeam = function(email, name, success, error) { +module.exports.signupTeam = function(email, display_name, success, error) { $.ajax({ url: "/api/v1/teams/signup", dataType: 'json', contentType: 'application/json', type: 'POST', - data: JSON.stringify({email: email, name: name}), + data: JSON.stringify({email: email, display_name: display_name}), success: success, error: function(xhr, status, err) { e = handleError("singupTeam", xhr, status, err); @@ -366,16 +373,16 @@ module.exports.createTeam = function(team, success, error) { }); }; -module.exports.findTeamByDomain = function(domain, success, error) { +module.exports.findTeamByName = function(teamName, success, error) { $.ajax({ - url: "/api/v1/teams/find_team_by_domain", + url: "/api/v1/teams/find_team_by_name", dataType: 'json', contentType: 'application/json', type: 'POST', - data: JSON.stringify({domain: domain}), + data: JSON.stringify({name: teamName}), success: success, error: function(xhr, status, err) { - e = handleError("findTeamByDomain", xhr, status, err); + e = handleError("findTeamByName", xhr, status, err); error(e); } }); diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx index 3aadfb4b0d..187e3c4a39 100644 --- a/web/react/utils/constants.jsx +++ b/web/react/utils/constants.jsx @@ -54,7 +54,7 @@ module.exports = { DEFAULT_CHANNEL: 'town-square', OFFTOPIC_CHANNEL: 'off-topic', POST_CHUNK_SIZE: 60, - RESERVED_DOMAINS: [ + RESERVED_TEAM_NAMES: [ "www", "web", "admin", diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 416ea5ae46..00580af6e3 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -16,10 +16,10 @@ module.exports.isEmail = function(email) { }; module.exports.cleanUpUrlable = function(input) { - var cleaned = input.trim().replace(/-/g, ' ').replace(/[^\w\s]/gi, '').toLowerCase().replace(/\s/g, '-'); - cleaned = cleaned.replace(/^\-+/, ''); - cleaned = cleaned.replace(/\-+$/, ''); - return cleaned; + var cleaned = input.trim().replace(/-/g, ' ').replace(/[^\w\s]/gi, '').toLowerCase().replace(/\s/g, '-'); + cleaned = cleaned.replace(/^\-+/, ''); + cleaned = cleaned.replace(/\-+$/, ''); + return cleaned; }; @@ -114,7 +114,7 @@ module.exports.notifyMe = function(title, body, channel) { if (channel) { module.exports.switchChannel(channel); } else { - window.location.href = "/channels/town-square"; + window.location.href = "/"; } }; setTimeout(function(){ @@ -708,8 +708,8 @@ module.exports.switchChannel = function(channel, teammate_name) { id: channel.id }); - var domain = window.location.href.split('/channels')[0]; - history.replaceState('data', '', domain + '/channels/' + channel.name); + var teamURL = window.location.href.split('/channels')[0]; + history.replaceState('data', '', teamURL + '/channels/' + channel.name); if (channel.type === 'D' && teammate_name) { document.title = teammate_name + " " + document.title.substring(document.title.lastIndexOf("-")); @@ -784,18 +784,6 @@ Image.prototype.load = function(url, progressCallback) { Image.prototype.completedPercentage = 0; -module.exports.getHomeLink = function() { - if (config.HomeLink != "") { - return config.HomeLink; - } - var parts = window.location.host.split("."); - if (parts.length <= 1) { - return window.location.protocol + "//" + window.location.host; - } - parts[0] = "www"; - return window.location.protocol + "//" + parts.join("."); -} - module.exports.changeColor =function(col, amt) { var usePound = false; diff --git a/web/sass-files/sass/partials/_headers.scss b/web/sass-files/sass/partials/_headers.scss index c2740891a9..adeaa70d71 100644 --- a/web/sass-files/sass/partials/_headers.scss +++ b/web/sass-files/sass/partials/_headers.scss @@ -98,6 +98,9 @@ fill: #fff; } } + .settings__link a:hover, a:visited, a:link, a:active { + text-decoration: none; + } .user__picture { width: 36px; height: 36px; @@ -248,4 +251,4 @@ margin-top: 8px; fill: #AAA; } -} \ No newline at end of file +} diff --git a/web/sass-files/sass/partials/_signup.scss b/web/sass-files/sass/partials/_signup.scss index a714aa44f6..98931279b8 100644 --- a/web/sass-files/sass/partials/_signup.scss +++ b/web/sass-files/sass/partials/_signup.scss @@ -6,7 +6,7 @@ } .signup-team__container { padding: 100px 0px 50px 0px; - max-width: 340px; + max-width: 600px; margin: 0 auto; font-size: 1.1em; position: relative; @@ -118,4 +118,4 @@ .signup-team-login { padding-bottom: 10px; font-weight: 700; -} \ No newline at end of file +} diff --git a/web/templates/channel.html b/web/templates/channel.html index d96aee3d45..eaf0f2563b 100644 --- a/web/templates/channel.html +++ b/web/templates/channel.html @@ -46,7 +46,7 @@
                  diff --git a/web/templates/home.html b/web/templates/home.html index abf8062f26..9ec8b70007 100644 --- a/web/templates/home.html +++ b/web/templates/home.html @@ -17,7 +17,7 @@
                  diff --git a/web/templates/login.html b/web/templates/login.html index c107e1ad5c..24cebec8f2 100644 --- a/web/templates/login.html +++ b/web/templates/login.html @@ -7,7 +7,7 @@
                  @@ -20,7 +20,7 @@
                  diff --git a/web/templates/password_reset.html b/web/templates/password_reset.html index 8b63556b1f..6244f6418f 100644 --- a/web/templates/password_reset.html +++ b/web/templates/password_reset.html @@ -9,7 +9,7 @@
                  diff --git a/web/templates/signup_team.html b/web/templates/signup_team.html index fad332beee..b865905891 100644 --- a/web/templates/signup_team.html +++ b/web/templates/signup_team.html @@ -12,7 +12,6 @@

                  All team communication in one place, searchable and accessible anywhere

                  {{ .SiteName }} is free for an unlimited time, for unlimited users

                  -
                  @@ -23,7 +22,7 @@ diff --git a/web/templates/signup_team_complete.html b/web/templates/signup_team_complete.html index 59f49cdbda..674e54ee2f 100644 --- a/web/templates/signup_team_complete.html +++ b/web/templates/signup_team_complete.html @@ -19,7 +19,7 @@ diff --git a/web/templates/signup_team_confirm.html b/web/templates/signup_team_confirm.html index 9e21126da8..3a6134af3b 100644 --- a/web/templates/signup_team_confirm.html +++ b/web/templates/signup_team_confirm.html @@ -8,7 +8,6 @@
                  diff --git a/web/web.go b/web/web.go index b11e6e6b11..3e4bc2d537 100644 --- a/web/web.go +++ b/web/web.go @@ -42,25 +42,28 @@ func (me *HtmlTemplatePage) Render(c *api.Context, w http.ResponseWriter) { func InitWeb() { l4g.Debug("Initializing web routes") + mainrouter := api.Srv.Router + staticDir := utils.FindDir("web/static") l4g.Debug("Using static directory at %v", staticDir) - api.Srv.Router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", - http.FileServer(http.Dir(staticDir)))) + mainrouter.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir)))) - api.Srv.Router.Handle("/", api.AppHandler(root)).Methods("GET") - api.Srv.Router.Handle("/login", api.AppHandler(login)).Methods("GET") - api.Srv.Router.Handle("/signup_team_confirm/", api.AppHandler(signupTeamConfirm)).Methods("GET") - api.Srv.Router.Handle("/signup_team_complete/", api.AppHandler(signupTeamComplete)).Methods("GET") - api.Srv.Router.Handle("/signup_user_complete/", api.AppHandler(signupUserComplete)).Methods("GET") + mainrouter.Handle("/", api.AppHandlerIndependent(root)).Methods("GET") + mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}", api.AppHandler(login)).Methods("GET") + mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/", api.AppHandler(login)).Methods("GET") + mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/login", api.AppHandler(login)).Methods("GET") + mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/logout", api.AppHandler(logout)).Methods("GET") + mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/reset_password", api.AppHandler(resetPassword)).Methods("GET") + // Bug in gorilla.mux pervents us from using regex here. + mainrouter.Handle("/{team}/channels/{channelname}", api.UserRequired(getChannel)).Methods("GET") - api.Srv.Router.Handle("/logout", api.AppHandler(logout)).Methods("GET") - - api.Srv.Router.Handle("/verify", api.AppHandler(verifyEmail)).Methods("GET") - api.Srv.Router.Handle("/find_team", api.AppHandler(findTeam)).Methods("GET") - api.Srv.Router.Handle("/reset_password", api.AppHandler(resetPassword)).Methods("GET") - - csr := api.Srv.Router.PathPrefix("/channels").Subrouter() - csr.Handle("/{name:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}", api.UserRequired(getChannel)).Methods("GET") + // Anything added here must have an _ in it so it does not conflict with team names + mainrouter.Handle("/signup_team_complete/", api.AppHandlerIndependent(signupTeamComplete)).Methods("GET") + mainrouter.Handle("/signup_user_complete/", api.AppHandlerIndependent(signupUserComplete)).Methods("GET") + mainrouter.Handle("/signup_team_confirm/", api.AppHandlerIndependent(signupTeamConfirm)).Methods("GET") + mainrouter.Handle("/verify_email", api.AppHandlerIndependent(verifyEmail)).Methods("GET") + mainrouter.Handle("/find_team", api.AppHandlerIndependent(findTeam)).Methods("GET") + mainrouter.Handle("/signup_team", api.AppHandlerIndependent(signup)).Methods("GET") watchAndParseTemplates() } @@ -128,46 +131,53 @@ func root(c *api.Context, w http.ResponseWriter, r *http.Request) { } if len(c.Session.UserId) == 0 { - if api.IsTestDomain(r) || strings.Index(r.Host, "www") == 0 || strings.Index(r.Host, "beta") == 0 || strings.Index(r.Host, "ci") == 0 { - page := NewHtmlTemplatePage("signup_team", "Signup") - page.Render(c, w) - } else { - login(c, w, r) - } + page := NewHtmlTemplatePage("signup_team", "Signup") + page.Render(c, w) } else { page := NewHtmlTemplatePage("home", "Home") + page.Props["TeamURL"] = c.GetTeamURL() page.Render(c, w) } } +func signup(c *api.Context, w http.ResponseWriter, r *http.Request) { + + if !CheckBrowserCompatability(c, r) { + return + } + + page := NewHtmlTemplatePage("signup_team", "Signup") + page.Render(c, w) +} + func login(c *api.Context, w http.ResponseWriter, r *http.Request) { if !CheckBrowserCompatability(c, r) { return } + params := mux.Vars(r) + teamName := params["team"] - teamName := "Beta" - teamDomain := "" - siteDomain := "." + utils.Cfg.ServiceSettings.Domain - - if utils.Cfg.ServiceSettings.Mode == utils.MODE_DEV { - teamDomain = "developer" - } else if utils.Cfg.ServiceSettings.Mode == utils.MODE_BETA { - teamDomain = "beta" + var team *model.Team + if tResult := <-api.Srv.Store.Team().GetByName(teamName); tResult.Err != nil { + l4g.Error("Couldn't find team name=%v, teamURL=%v, err=%v", teamName, c.GetTeamURL(), tResult.Err.Message) + // This should probably do somthing nicer + http.Redirect(w, r, "http://"+r.Host, http.StatusTemporaryRedirect) + return } else { - teamDomain, siteDomain = model.GetSubDomain(c.TeamUrl) - siteDomain = "." + siteDomain + ".com" + team = tResult.Data.(*model.Team) + } - if tResult := <-api.Srv.Store.Team().GetByDomain(teamDomain); tResult.Err != nil { - l4g.Error("Couldn't find team teamDomain=%v, siteDomain=%v, teamUrl=%v, err=%v", teamDomain, siteDomain, c.TeamUrl, tResult.Err.Message) - } else { - teamName = tResult.Data.(*model.Team).Name - } + // If we are already logged into this team then go to home + if len(c.Session.UserId) != 0 && c.Session.TeamId == team.Id { + page := NewHtmlTemplatePage("home", "Home") + page.Props["TeamURL"] = c.GetTeamURL() + page.Render(c, w) + return } page := NewHtmlTemplatePage("login", "Login") + page.Props["TeamDisplayName"] = team.DisplayName page.Props["TeamName"] = teamName - page.Props["TeamDomain"] = teamDomain - page.Props["SiteDomain"] = siteDomain page.Render(c, w) } @@ -198,7 +208,7 @@ func signupTeamComplete(c *api.Context, w http.ResponseWriter, r *http.Request) page := NewHtmlTemplatePage("signup_team_complete", "Complete Team Sign Up") page.Props["Email"] = props["email"] - page.Props["Name"] = props["name"] + page.Props["DisplayName"] = props["display_name"] page.Props["Data"] = data page.Props["Hash"] = hash page.Render(c, w) @@ -225,8 +235,8 @@ func signupUserComplete(c *api.Context, w http.ResponseWriter, r *http.Request) } props["email"] = "" + props["display_name"] = team.DisplayName props["name"] = team.Name - props["domain"] = team.Domain props["id"] = team.Id data = model.MapToJson(props) hash = "" @@ -249,8 +259,8 @@ func signupUserComplete(c *api.Context, w http.ResponseWriter, r *http.Request) page := NewHtmlTemplatePage("signup_user_complete", "Complete User Sign Up") page.Props["Email"] = props["email"] + page.Props["TeamDisplayName"] = props["display_name"] page.Props["TeamName"] = props["name"] - page.Props["TeamDomain"] = props["domain"] page.Props["TeamId"] = props["id"] page.Props["Data"] = data page.Props["Hash"] = hash @@ -259,12 +269,12 @@ func signupUserComplete(c *api.Context, w http.ResponseWriter, r *http.Request) func logout(c *api.Context, w http.ResponseWriter, r *http.Request) { api.Logout(c, w, r) - http.Redirect(w, r, "/", http.StatusFound) + http.Redirect(w, r, c.GetTeamURL(), http.StatusFound) } func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - name := params["name"] + name := params["channelname"] var channelId string if result := <-api.Srv.Store.Channel().CheckPermissionsToByName(c.Session.TeamId, name, c.Session.UserId); result.Err != nil { @@ -304,7 +314,7 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) { //api.Handle404(w, r) //Bad channel urls just redirect to the town-square for now - http.Redirect(w, r, "/channels/town-square", http.StatusFound) + http.Redirect(w, r, c.GetTeamURL()+"/channels/town-square", http.StatusFound) return } } @@ -319,8 +329,8 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) { } page := NewHtmlTemplatePage("channel", "") - page.Title = name + " - " + team.Name + " " + page.SiteName - page.Props["TeamName"] = team.Name + page.Title = name + " - " + team.DisplayName + " " + page.SiteName + page.Props["TeamDisplayName"] = team.DisplayName page.Props["TeamType"] = team.Type page.Props["TeamId"] = team.Id page.Props["ChannelName"] = name @@ -331,7 +341,7 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) { func verifyEmail(c *api.Context, w http.ResponseWriter, r *http.Request) { resend := r.URL.Query().Get("resend") - domain := r.URL.Query().Get("domain") + name := r.URL.Query().Get("name") email := r.URL.Query().Get("email") hashedId := r.URL.Query().Get("hid") userId := r.URL.Query().Get("uid") @@ -339,7 +349,7 @@ func verifyEmail(c *api.Context, w http.ResponseWriter, r *http.Request) { if resend == "true" { teamId := "" - if result := <-api.Srv.Store.Team().GetByDomain(domain); result.Err != nil { + if result := <-api.Srv.Store.Team().GetByName(name); result.Err != nil { c.Err = result.Err return } else { @@ -351,7 +361,7 @@ func verifyEmail(c *api.Context, w http.ResponseWriter, r *http.Request) { return } else { user := result.Data.(*model.User) - api.FireAndForgetVerifyEmail(user.Id, user.FirstName, user.Email, domain, c.TeamUrl) + api.FireAndForgetVerifyEmail(user.Id, strings.Split(user.Nickname, " ")[0], user.Email, name, c.GetTeamURL()) http.Redirect(w, r, "/", http.StatusFound) return } @@ -387,6 +397,8 @@ func resetPassword(c *api.Context, w http.ResponseWriter, r *http.Request) { isResetLink := true hash := r.URL.Query().Get("h") data := r.URL.Query().Get("d") + params := mux.Vars(r) + teamName := params["team"] if len(hash) == 0 || len(data) == 0 { isResetLink = false @@ -405,30 +417,25 @@ func resetPassword(c *api.Context, w http.ResponseWriter, r *http.Request) { } } - teamName := "Developer/Beta" - domain := "" - if utils.Cfg.ServiceSettings.Mode != utils.MODE_DEV { - domain, _ = model.GetSubDomain(c.TeamUrl) + teamDisplayName := "Developer/Beta" + var team *model.Team + if tResult := <-api.Srv.Store.Team().GetByName(teamName); tResult.Err != nil { + c.Err = tResult.Err + return + } else { + team = tResult.Data.(*model.Team) + } - var team *model.Team - if tResult := <-api.Srv.Store.Team().GetByDomain(domain); tResult.Err != nil { - c.Err = tResult.Err - return - } else { - team = tResult.Data.(*model.Team) - } - - if team != nil { - teamName = team.Name - } + if team != nil { + teamDisplayName = team.DisplayName } page := NewHtmlTemplatePage("password_reset", "") page.Title = "Reset Password - " + page.SiteName - page.Props["TeamName"] = teamName + page.Props["TeamDisplayName"] = teamDisplayName page.Props["Hash"] = hash page.Props["Data"] = data - page.Props["Domain"] = domain + page.Props["TeamName"] = teamName page.Props["IsReset"] = strconv.FormatBool(isResetLink) page.Render(c, w) } From 4b7fda14ba76fc4244204f8d2b95753f4b46ea22 Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Tue, 21 Jul 2015 16:47:08 +0500 Subject: [PATCH 89/90] mm-1631 - Fixing image previewer bug --- web/react/components/create_post.jsx | 2 +- web/react/components/view_image.jsx | 9 ++++++--- web/sass-files/sass/partials/_post.scss | 6 ++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx index 0c23dcfac0..d38a6798f9 100644 --- a/web/react/components/create_post.jsx +++ b/web/react/components/create_post.jsx @@ -234,7 +234,7 @@ module.exports = React.createClass({ }, render: function() { - var server_error = this.state.server_error ?
                  : null; + var server_error = this.state.server_error ?
                  : null; var post_error = this.state.post_error ? : null; var limit_error = this.state.limit_error ?
                  : null; diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx index 38f439946b..2274f3f2e6 100644 --- a/web/react/components/view_image.jsx +++ b/web/react/components/view_image.jsx @@ -25,7 +25,11 @@ module.exports = React.createClass({ this.setState({ imgId: nextProps.startId }); }, loadImage: function(id) { - if (this.state.loaded[id] || this.state.images[id]) return; + var imgHeight = $(window).height()-100; + if (this.state.loaded[id] || this.state.images[id]){ + $('.modal .modal-image .image-wrapper img').css("max-height",imgHeight); + return; + }; var src = ""; if (this.props.imgCount > 0) { @@ -48,6 +52,7 @@ module.exports = React.createClass({ var loaded = self.state.loaded; loaded[imgid] = true; self.setState({ loaded: loaded }); + $(self.refs.image.getDOMNode()).css("max-height",imgHeight); }; }(id); var images = this.state.images; @@ -56,10 +61,8 @@ module.exports = React.createClass({ }, componentDidUpdate: function() { if (this.refs.image) { - var height = $(window).height()-100; if (this.state.loaded[this.state.imgId]) { $(this.refs.imageWrap.getDOMNode()).removeClass("default"); - $(this.refs.image.getDOMNode()).css("max-height",height); } } }, diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss index 9368786d1a..481ed63a52 100644 --- a/web/sass-files/sass/partials/_post.scss +++ b/web/sass-files/sass/partials/_post.scss @@ -187,6 +187,12 @@ body.ios { .post-create-footer { @include clearfix; padding: 0; + .has-error { + .control-label { + font-weight: normal; + margin-bottom: 0; + } + } .msg-typing { min-height: 20px; line-height: 18px; From cf3bfd24fdf9b51f0215c87eafc34ad9aa0f6132 Mon Sep 17 00:00:00 2001 From: JoramWilander Date: Tue, 21 Jul 2015 10:49:37 -0400 Subject: [PATCH 90/90] quick fix to get manage team modal working again --- web/react/components/member_list_team.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/web/react/components/member_list_team.jsx b/web/react/components/member_list_team.jsx index 6f1d83193a..cb48e5cc59 100644 --- a/web/react/components/member_list_team.jsx +++ b/web/react/components/member_list_team.jsx @@ -5,6 +5,7 @@ var ChannelStore = require('../stores/channel_store.jsx'); var UserStore = require('../stores/user_store.jsx'); var Client = require('../utils/client.jsx'); var AsyncClient = require('../utils/async_client.jsx'); +var utils = require('../utils/utils.jsx'); var MemberListTeamItem = React.createClass({ handleMakeMember: function() {