From 1ab7034574de2229d3cfc49391e6579db37a3064 Mon Sep 17 00:00:00 2001 From: Antti Ahti Date: Wed, 14 Oct 2015 18:27:03 +0300 Subject: Use team display name in team switcher menu - /teams/find_teams returns team objects instead of just team names - use display_name in UI instead of name in the team switch menu --- api/team.go | 8 +++----- api/team_test.go | 9 ++++++--- model/client.go | 2 +- web/react/components/navbar_dropdown.jsx | 13 ++++++++----- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/api/team.go b/api/team.go index 6aa5ec1bb..7148caac3 100644 --- a/api/team.go +++ b/api/team.go @@ -409,14 +409,12 @@ func findTeams(c *Context, w http.ResponseWriter, r *http.Request) { return } else { teams := result.Data.([]*model.Team) - - s := make([]string, 0, len(teams)) - + m := make(map[string]*model.Team) for _, v := range teams { - s = append(s, v.Name) + m[v.Id] = v } - w.Write([]byte(model.ArrayToJson(s))) + w.Write([]byte(model.TeamMapToJson(m))) } } diff --git a/api/team_test.go b/api/team_test.go index 9b701911b..507f4252a 100644 --- a/api/team_test.go +++ b/api/team_test.go @@ -121,9 +121,12 @@ func TestFindTeamByEmail(t *testing.T) { if r1, err := Client.FindTeams(user.Email); err != nil { t.Fatal(err) } else { - domains := r1.Data.([]string) - if domains[0] != team.Name { - t.Fatal(domains) + teams := r1.Data.(map[string]*model.Team) + if teams[team.Id].Name != team.Name { + t.Fatal() + } + if teams[team.Id].DisplayName != team.DisplayName { + t.Fatal() } } diff --git a/model/client.go b/model/client.go index 77b0aaad2..19c99df72 100644 --- a/model/client.go +++ b/model/client.go @@ -185,7 +185,7 @@ func (c *Client) FindTeams(email string) (*Result, *AppError) { } else { return &Result{r.Header.Get(HEADER_REQUEST_ID), - r.Header.Get(HEADER_ETAG_SERVER), ArrayFromJson(r.Body)}, nil + r.Header.Get(HEADER_ETAG_SERVER), TeamMapFromJson(r.Body)}, nil } } diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx index 49d517419..b3f8e4418 100644 --- a/web/react/components/navbar_dropdown.jsx +++ b/web/react/components/navbar_dropdown.jsx @@ -145,7 +145,7 @@ export default class NavbarDropdown extends React.Component { var teams = []; - if (this.state.teams.length > 1) { + if (Object.keys(this.state.teams).length > 1) { teams.push(
  • ); - this.state.teams.forEach((teamName) => { - if (teamName !== this.props.teamName) { - teams.push(
  • {'Switch to ' + teamName}
  • ); + for (let teamId in this.state.teams) { + if (this.state.teams.hasOwnProperty(teamId)) { + let team = this.state.teams[teamId]; + if (team.name !== this.props.teamName) { + teams.push(
  • {'Switch to ' + team.display_name}
  • ); + } } - }); + } } if (global.window.config.EnableTeamCreation === 'true') { -- cgit v1.2.3-1-g7c22 From 731595c1a93e51bcd4e6034b088b53766883a9eb Mon Sep 17 00:00:00 2001 From: Antti Ahti Date: Wed, 14 Oct 2015 18:31:58 +0300 Subject: Fix typo (isTreamCreationAllowed -> isTeamCreationAllowed) --- api/team.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/api/team.go b/api/team.go index 7148caac3..18acf33ce 100644 --- a/api/team.go +++ b/api/team.go @@ -52,7 +52,7 @@ func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !isTreamCreationAllowed(c, email) { + if !isTeamCreationAllowed(c, email) { return } @@ -100,7 +100,7 @@ func createTeamFromSSO(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !isTreamCreationAllowed(c, team.Email) { + if !isTeamCreationAllowed(c, team.Email) { return } @@ -169,7 +169,7 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !isTreamCreationAllowed(c, teamSignup.Team.Email) { + if !isTeamCreationAllowed(c, teamSignup.Team.Email) { return } @@ -257,7 +257,7 @@ func CreateTeam(c *Context, team *model.Team) *model.Team { return nil } - if !isTreamCreationAllowed(c, team.Email) { + if !isTeamCreationAllowed(c, team.Email) { return nil } @@ -276,12 +276,12 @@ func CreateTeam(c *Context, team *model.Team) *model.Team { } } -func isTreamCreationAllowed(c *Context, email string) bool { +func isTeamCreationAllowed(c *Context, email string) bool { email = strings.ToLower(email) if !utils.Cfg.TeamSettings.EnableTeamCreation { - c.Err = model.NewAppError("isTreamCreationAllowed", "Team creation has been disabled. Please ask your systems administrator for details.", "") + c.Err = model.NewAppError("isTeamCreationAllowed", "Team creation has been disabled. Please ask your systems administrator for details.", "") return false } @@ -298,7 +298,7 @@ func isTreamCreationAllowed(c *Context, email string) bool { } if len(utils.Cfg.TeamSettings.RestrictCreationToDomains) > 0 && !matched { - c.Err = model.NewAppError("isTreamCreationAllowed", "Email must be from a specific domain (e.g. @example.com). Please ask your systems administrator for details.", "") + c.Err = model.NewAppError("isTeamCreationAllowed", "Email must be from a specific domain (e.g. @example.com). Please ask your systems administrator for details.", "") return false } -- cgit v1.2.3-1-g7c22 From 8f96db4d0b52d0701398703364948e659f3ce34f Mon Sep 17 00:00:00 2001 From: Antti Ahti Date: Wed, 14 Oct 2015 19:27:19 +0300 Subject: Sanitize team data We don't want to expose sensitive data that might pose a security risk. --- api/team.go | 1 + model/team.go | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/api/team.go b/api/team.go index 18acf33ce..f6038566a 100644 --- a/api/team.go +++ b/api/team.go @@ -411,6 +411,7 @@ func findTeams(c *Context, w http.ResponseWriter, r *http.Request) { teams := result.Data.([]*model.Team) m := make(map[string]*model.Team) for _, v := range teams { + v.Sanitize() m[v.Id] = v } diff --git a/model/team.go b/model/team.go index c0f6524cd..584c78f8d 100644 --- a/model/team.go +++ b/model/team.go @@ -219,3 +219,9 @@ func CleanTeamName(s string) string { func (o *Team) PreExport() { } + +func (o *Team) Sanitize() { + o.Email = "" + o.Type = "" + o.AllowedDomains = "" +} -- cgit v1.2.3-1-g7c22 From 76e278a13de936c09068ad1c2c2a16be0c419249 Mon Sep 17 00:00:00 2001 From: Stas Vovk Date: Wed, 14 Oct 2015 20:36:12 +0300 Subject: added display tab under account settings. added 24h time option --- web/react/components/post_list.jsx | 3 ++ .../components/user_settings/user_settings.jsx | 12 ++++++++ .../user_settings/user_settings_modal.jsx | 1 + web/react/utils/constants.jsx | 3 +- web/react/utils/utils.jsx | 33 +++++++++++++--------- 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index 643b38af5..ebbe319b5 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -4,6 +4,7 @@ var PostStore = require('../stores/post_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx'); var UserStore = require('../stores/user_store.jsx'); +var PreferenceStore = require('../stores/preference_store.jsx'); var UserProfile = require('./user_profile.jsx'); var AsyncClient = require('../utils/async_client.jsx'); var Post = require('./post.jsx'); @@ -105,6 +106,7 @@ export default class PostList extends React.Component { PostStore.clearUnseenDeletedPosts(this.props.channelId); PostStore.addChangeListener(this.onChange); UserStore.addStatusesChangeListener(this.onTimeChange); + PreferenceStore.addChangeListener(this.onTimeChange); SocketStore.addChangeListener(this.onSocketChange); const postHolder = $(React.findDOMNode(this.refs.postlist)); @@ -156,6 +158,7 @@ export default class PostList extends React.Component { PostStore.removeChangeListener(this.onChange); UserStore.removeStatusesChangeListener(this.onTimeChange); SocketStore.removeChangeListener(this.onSocketChange); + PreferenceStore.removeChangeListener(this.onTimeChange); $('body').off('click.userpopover'); $(window).off('resize'); var postHolder = $(React.findDOMNode(this.refs.postlist)); diff --git a/web/react/components/user_settings/user_settings.jsx b/web/react/components/user_settings/user_settings.jsx index 5ce9b6330..15bf961d6 100644 --- a/web/react/components/user_settings/user_settings.jsx +++ b/web/react/components/user_settings/user_settings.jsx @@ -9,6 +9,7 @@ var GeneralTab = require('./user_settings_general.jsx'); var AppearanceTab = require('./user_settings_appearance.jsx'); var DeveloperTab = require('./user_settings_developer.jsx'); var IntegrationsTab = require('./user_settings_integrations.jsx'); +var DisplayTab = require('./user_settings_display.jsx'); export default class UserSettings extends React.Component { constructor(props) { @@ -98,6 +99,17 @@ export default class UserSettings extends React.Component { /> ); + } else if (this.props.activeTab === 'display') { + return ( +
    + +
    + ); } return
    ; diff --git a/web/react/components/user_settings/user_settings_modal.jsx b/web/react/components/user_settings/user_settings_modal.jsx index 19b97fc85..692fb26ee 100644 --- a/web/react/components/user_settings/user_settings_modal.jsx +++ b/web/react/components/user_settings/user_settings_modal.jsx @@ -41,6 +41,7 @@ export default class UserSettingsModal extends React.Component { if (global.window.config.EnableIncomingWebhooks === 'true') { tabs.push({name: 'integrations', uiName: 'Integrations', icon: 'glyphicon glyphicon-transfer'}); } + tabs.push({name: 'display', uiName: 'Display', icon: 'glyphicon glyphicon-eye-open'}); return (
    = 12) { - ampm = 'PM'; - } + const d = new Date(ticks); + let hours = d.getHours(); + let minutes = d.getMinutes(); + let ampm = ''; - hours = hours % 12; - if (!hours) { - hours = '12'; - } if (minutes <= 9) { minutes = '0' + minutes; } - return hours + ':' + minutes + ' ' + ampm; + + const useMilitaryTime = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', {value: 'false'}).value; + if(useMilitaryTime === 'false') { + ampm = ' AM'; + if (hours >= 12) { + ampm = ' PM'; + } + + hours = hours % 12; + if (!hours) { + hours = '12'; + } + } + + return hours + ':' + minutes + ampm; } export function displayDateTime(ticks) { -- cgit v1.2.3-1-g7c22 From 29a0c182ef376de8368552e7b472afc960c4ba8a Mon Sep 17 00:00:00 2001 From: Stas Vovk Date: Wed, 14 Oct 2015 21:39:33 +0300 Subject: added file with display settings --- .../user_settings/user_settings_display.jsx | 168 +++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 web/react/components/user_settings/user_settings_display.jsx diff --git a/web/react/components/user_settings/user_settings_display.jsx b/web/react/components/user_settings/user_settings_display.jsx new file mode 100644 index 000000000..ec209c218 --- /dev/null +++ b/web/react/components/user_settings/user_settings_display.jsx @@ -0,0 +1,168 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import { savePreferences } from '../../utils/client.jsx'; +import SettingItemMin from '../setting_item_min.jsx'; +import SettingItemMax from '../setting_item_max.jsx'; +import Constants from '../../utils/constants.jsx'; +import PreferenceStore from '../../stores/preference_store.jsx'; + +function getDisplayStateFromStores() { + const militaryTime = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', {value: 'false'}); + + return {militaryTime: militaryTime.value}; +} + +export default class UserSettingsDisplay extends React.Component { + constructor(props) { + super(props); + + this.handleSubmit = this.handleSubmit.bind(this); + this.handleClockRadio = this.handleClockRadio.bind(this); + this.updateSection = this.updateSection.bind(this); + this.handleClose = this.handleClose.bind(this); + + this.state = getDisplayStateFromStores(); + } + handleSubmit() { + const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', this.state.militaryTime); + + savePreferences([preference], + () => { + PreferenceStore.emitChange(); + this.updateSection(''); + }, + (err) => { + this.setState({serverError: err.message}); + } + ); + } + handleClockRadio(militaryTime) { + this.setState({militaryTime: militaryTime}); + } + updateSection(section) { + this.setState(getDisplayStateFromStores()); + this.props.updateSection(section); + } + handleClose() { + this.updateSection(''); + } + componentDidMount() { + $('#user_settings').on('hidden.bs.modal', this.handleClose); + } + componentWillUnmount() { + $('#user_settings').off('hidden.bs.modal', this.handleClose); + } + render() { + const serverError = this.state.serverError || null; + let clockSection; + if (this.props.activeSection === 'clock') { + let clockFormat = [false, false]; + if (this.state.militaryTime === 'true') { + clockFormat[1] = true; + } else { + clockFormat[0] = true; + } + + const handleUpdateClockSection = (e) => { + this.updateSection(''); + e.preventDefault(); + }; + + const inputs = [ +
    +
    + +
    +
    +
    + +
    +
    +

    {'Select how you prefer time displayed.'}
    +
    + ]; + + + clockSection = ( + + ); + } else { + let describe = ''; + if (this.state.militaryTime === 'true') { + describe = '24-hour clock (example: 16:00)'; + } else { + describe = '12-hour clock (example: 4:00 PM)'; + } + + const handleUpdateClockSection = () => { + this.props.updateSection('clock'); + }; + + clockSection = ( + + ); + } + + return ( +
    +
    + +

    + + {'Display Settings'} +

    +
    +
    +

    {'Display Settings'}

    +
    + {clockSection} +
    +
    +
    + ); + } +} + +UserSettingsDisplay.propTypes = { + user: React.PropTypes.object, + updateSection: React.PropTypes.func, + updateTab: React.PropTypes.func, + activeSection: React.PropTypes.string +}; -- cgit v1.2.3-1-g7c22 From 440faabc604fa6763515efd9009a7c276e140f09 Mon Sep 17 00:00:00 2001 From: Stas Vovk Date: Thu, 15 Oct 2015 00:05:39 +0300 Subject: fixed style guide error --- 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 a5057b5ca..8f01b3e33 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -175,7 +175,7 @@ export function displayTime(ticks) { } const useMilitaryTime = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', {value: 'false'}).value; - if(useMilitaryTime === 'false') { + if (useMilitaryTime === 'false') { ampm = ' AM'; if (hours >= 12) { ampm = ' PM'; -- cgit v1.2.3-1-g7c22 From 059df3de0126f2a506b525f92911035eba02bbd3 Mon Sep 17 00:00:00 2001 From: Antti Ahti Date: Thu, 15 Oct 2015 08:41:52 +0300 Subject: Sort teams by display name First we need to convert the object to array, because objects cannot be sorted. --- web/react/components/navbar_dropdown.jsx | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx index b3f8e4418..d4308ad72 100644 --- a/web/react/components/navbar_dropdown.jsx +++ b/web/react/components/navbar_dropdown.jsx @@ -11,7 +11,25 @@ var AboutBuildModal = require('./about_build_modal.jsx'); var Constants = require('../utils/constants.jsx'); function getStateFromStores() { - return {teams: UserStore.getTeams()}; + let teams = []; + let teamsObject = UserStore.getTeams(); + for (let teamId in teamsObject) { + if (teamsObject.hasOwnProperty(teamId)) { + teams.push(teamsObject[teamId]) + } + } + teams.sort(function (teamA, teamB) { + let teamADisplayName = teamA.display_name.toLowerCase(); + let teamBDisplayName = teamB.display_name.toLowerCase(); + if (teamADisplayName < teamBDisplayName) { + return -1 + } else if (teamADisplayName > teamBDisplayName) { + return 1; + } else { + return 0; + } + }); + return {teams}; } export default class NavbarDropdown extends React.Component { -- cgit v1.2.3-1-g7c22 From c0809bbc3fc7a9630659872850c313eb6df701d9 Mon Sep 17 00:00:00 2001 From: Antti Ahti Date: Thu, 15 Oct 2015 08:54:20 +0300 Subject: Treat teams as an array instead of object --- web/react/components/navbar_dropdown.jsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx index d4308ad72..564a64f48 100644 --- a/web/react/components/navbar_dropdown.jsx +++ b/web/react/components/navbar_dropdown.jsx @@ -163,7 +163,7 @@ export default class NavbarDropdown extends React.Component { var teams = []; - if (Object.keys(this.state.teams).length > 1) { + if (this.state.teams.length > 1) { teams.push(
  • ); - for (let teamId in this.state.teams) { - if (this.state.teams.hasOwnProperty(teamId)) { - let team = this.state.teams[teamId]; - if (team.name !== this.props.teamName) { - teams.push(
  • {'Switch to ' + team.display_name}
  • ); - } + this.state.teams.forEach((team) => { + if (team.name !== this.props.teamName) { + teams.push(
  • {'Switch to ' + team.display_name}
  • ); } - } + }); } if (global.window.config.EnableTeamCreation === 'true') { -- cgit v1.2.3-1-g7c22 From 7abde676a1828714c71b25184fdd91a81c2d77a1 Mon Sep 17 00:00:00 2001 From: Antti Ahti Date: Thu, 15 Oct 2015 19:35:45 +0300 Subject: Fix lint --- web/react/components/navbar_dropdown.jsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx index 564a64f48..dc41775c7 100644 --- a/web/react/components/navbar_dropdown.jsx +++ b/web/react/components/navbar_dropdown.jsx @@ -15,19 +15,18 @@ function getStateFromStores() { let teamsObject = UserStore.getTeams(); for (let teamId in teamsObject) { if (teamsObject.hasOwnProperty(teamId)) { - teams.push(teamsObject[teamId]) + teams.push(teamsObject[teamId]); } } - teams.sort(function (teamA, teamB) { + teams.sort(function sortByDisplayName(teamA, teamB) { let teamADisplayName = teamA.display_name.toLowerCase(); let teamBDisplayName = teamB.display_name.toLowerCase(); if (teamADisplayName < teamBDisplayName) { - return -1 + return -1; } else if (teamADisplayName > teamBDisplayName) { return 1; - } else { - return 0; } + return 0; }); return {teams}; } -- cgit v1.2.3-1-g7c22 From 2bd81ff379acb347534e4d6a1a27624e0262deb0 Mon Sep 17 00:00:00 2001 From: Stas Vovk Date: Thu, 15 Oct 2015 19:49:34 +0300 Subject: update posts time in the right sidebar when user changes time format --- web/react/components/rhs_thread.jsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/web/react/components/rhs_thread.jsx b/web/react/components/rhs_thread.jsx index 41fd74adb..748cb16a8 100644 --- a/web/react/components/rhs_thread.jsx +++ b/web/react/components/rhs_thread.jsx @@ -3,6 +3,7 @@ var PostStore = require('../stores/post_store.jsx'); var UserStore = require('../stores/user_store.jsx'); +var PreferenceStore = require('../stores/preference_store.jsx'); var utils = require('../utils/utils.jsx'); var SearchBox = require('./search_bar.jsx'); var CreateComment = require('./create_comment.jsx'); @@ -18,6 +19,7 @@ export default class RhsThread extends React.Component { this.onChange = this.onChange.bind(this); this.onChangeAll = this.onChangeAll.bind(this); + this.forceUpdateInfo = this.forceUpdateInfo.bind(this); this.state = this.getStateFromStores(); } @@ -43,6 +45,7 @@ export default class RhsThread extends React.Component { componentDidMount() { PostStore.addSelectedPostChangeListener(this.onChange); PostStore.addChangeListener(this.onChangeAll); + PreferenceStore.addChangeListener(this.forceUpdateInfo); this.resize(); $(window).resize(function resize() { this.resize(); @@ -59,6 +62,16 @@ export default class RhsThread extends React.Component { componentWillUnmount() { PostStore.removeSelectedPostChangeListener(this.onChange); PostStore.removeChangeListener(this.onChangeAll); + PreferenceStore.removeChangeListener(this.forceUpdateInfo); + } + forceUpdateInfo() { + if (this.state.postList) { + for (var postId in this.state.postList.posts) { + if (this.refs[postId]) { + this.refs[postId].forceUpdate(); + } + } + } } onChange() { var newState = this.getStateFromStores(); @@ -174,6 +187,7 @@ export default class RhsThread extends React.Component { />
    -- cgit v1.2.3-1-g7c22 From b438eefad1a4303aeb5ab60214204c074e260224 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Thu, 15 Oct 2015 14:52:59 -0400 Subject: Removed preference migration code --- api/preference.go | 51 -------------------------------------------------- api/preference_test.go | 48 ----------------------------------------------- api/user.go | 43 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 42 insertions(+), 100 deletions(-) diff --git a/api/preference.go b/api/preference.go index 88cb132f8..3a0535473 100644 --- a/api/preference.go +++ b/api/preference.go @@ -52,61 +52,10 @@ func getPreferenceCategory(c *Context, w http.ResponseWriter, r *http.Request) { } else { data := result.Data.(model.Preferences) - data = transformPreferences(c, data, category) - w.Write([]byte(data.ToJson())) } } -func transformPreferences(c *Context, preferences model.Preferences, category string) model.Preferences { - if len(preferences) == 0 && category == model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW { - // add direct channels for a user that existed before preferences were added - preferences = addDirectChannels(c.Session.UserId, c.Session.TeamId) - } - - return preferences -} - -func addDirectChannels(userId, teamId string) model.Preferences { - var profiles map[string]*model.User - if result := <-Srv.Store.User().GetProfiles(teamId); result.Err != nil { - l4g.Error("Failed to add direct channel preferences for user user_id=%s, team_id=%s, err=%v", userId, teamId, result.Err.Error()) - return model.Preferences{} - } else { - profiles = result.Data.(map[string]*model.User) - } - - var preferences model.Preferences - - for id := range profiles { - if id == userId { - continue - } - - profile := profiles[id] - - preference := model.Preference{ - UserId: userId, - Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, - Name: profile.Id, - Value: "true", - } - - preferences = append(preferences, preference) - - if len(preferences) >= 10 { - break - } - } - - if result := <-Srv.Store.Preference().Save(&preferences); result.Err != nil { - l4g.Error("Failed to add direct channel preferences for user user_id=%s, eam_id=%s, err=%v", userId, teamId, result.Err.Error()) - return model.Preferences{} - } else { - return preferences - } -} - func getPreference(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) category := params["category"] diff --git a/api/preference_test.go b/api/preference_test.go index 318ce9582..dfdf8b581 100644 --- a/api/preference_test.go +++ b/api/preference_test.go @@ -113,54 +113,6 @@ func TestGetPreferenceCategory(t *testing.T) { } } -func TestTransformPreferences(t *testing.T) { - Setup() - - 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)) - - for i := 0; i < 5; i++ { - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} - Client.Must(Client.CreateUser(user, "")) - } - - Client.Must(Client.LoginByEmail(team.Name, user1.Email, "pwd")) - - if result, err := Client.GetPreferenceCategory(model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW); err != nil { - t.Fatal(err) - } else if data := result.Data.(model.Preferences); len(data) != 5 { - t.Fatal("received the wrong number of direct channels") - } - - 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)) - - for i := 0; i < 10; i++ { - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} - Client.Must(Client.CreateUser(user, "")) - } - - // make sure user1's preferences don't change - if result, err := Client.GetPreferenceCategory(model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW); err != nil { - t.Fatal(err) - } else if data := result.Data.(model.Preferences); len(data) != 5 { - t.Fatal("received the wrong number of direct channels") - } - - Client.Must(Client.LoginByEmail(team.Name, user2.Email, "pwd")) - - if result, err := Client.GetPreferenceCategory(model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW); err != nil { - t.Fatal(err) - } else if data := result.Data.(model.Preferences); len(data) != 10 { - t.Fatal("received the wrong number of direct channels") - } -} - func TestGetPreference(t *testing.T) { Setup() diff --git a/api/user.go b/api/user.go index 146ede015..ac33e81a1 100644 --- a/api/user.go +++ b/api/user.go @@ -198,7 +198,9 @@ 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(result.Data.(*model.User).Id, ruser.Email, team.Name, team.DisplayName, c.GetSiteURL(), c.GetTeamURLFromTeam(team), user.EmailVerified) + fireAndForgetWelcomeEmail(ruser.Id, ruser.Email, team.Name, team.DisplayName, c.GetSiteURL(), c.GetTeamURLFromTeam(team), user.EmailVerified) + + addDirectChannelsAndForget(ruser) if user.EmailVerified { if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { @@ -237,6 +239,45 @@ func fireAndForgetWelcomeEmail(userId, email, teamName, teamDisplayName, siteURL }() } +func addDirectChannelsAndForget(user *model.User) { + go func() { + var profiles map[string]*model.User + if result := <-Srv.Store.User().GetProfiles(user.TeamId); result.Err != nil { + l4g.Error("Failed to add direct channel preferences for user user_id=%s, team_id=%s, err=%v", user.Id, user.TeamId, result.Err.Error()) + return + } else { + profiles = result.Data.(map[string]*model.User) + } + + var preferences model.Preferences + + for id := range profiles { + if id == user.Id { + continue + } + + profile := profiles[id] + + preference := model.Preference{ + UserId: user.Id, + Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, + Name: profile.Id, + Value: "true", + } + + preferences = append(preferences, preference) + + if len(preferences) >= 10 { + break + } + } + + if result := <-Srv.Store.Preference().Save(&preferences); result.Err != nil { + l4g.Error("Failed to add direct channel preferences for new user user_id=%s, eam_id=%s, err=%v", user.Id, user.TeamId, result.Err.Error()) + } + }() +} + func FireAndForgetVerifyEmail(userId, userEmail, teamName, teamDisplayName, siteURL, teamURL string) { go func() { -- cgit v1.2.3-1-g7c22 From 4b99e1714029689f27ebf4cb078c60367b0594a4 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Thu, 15 Oct 2015 14:52:59 -0400 Subject: Removed preference migration code --- api/preference.go | 51 -------------------------------------------------- api/preference_test.go | 48 ----------------------------------------------- api/user.go | 39 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 99 deletions(-) diff --git a/api/preference.go b/api/preference.go index 88cb132f8..3a0535473 100644 --- a/api/preference.go +++ b/api/preference.go @@ -52,61 +52,10 @@ func getPreferenceCategory(c *Context, w http.ResponseWriter, r *http.Request) { } else { data := result.Data.(model.Preferences) - data = transformPreferences(c, data, category) - w.Write([]byte(data.ToJson())) } } -func transformPreferences(c *Context, preferences model.Preferences, category string) model.Preferences { - if len(preferences) == 0 && category == model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW { - // add direct channels for a user that existed before preferences were added - preferences = addDirectChannels(c.Session.UserId, c.Session.TeamId) - } - - return preferences -} - -func addDirectChannels(userId, teamId string) model.Preferences { - var profiles map[string]*model.User - if result := <-Srv.Store.User().GetProfiles(teamId); result.Err != nil { - l4g.Error("Failed to add direct channel preferences for user user_id=%s, team_id=%s, err=%v", userId, teamId, result.Err.Error()) - return model.Preferences{} - } else { - profiles = result.Data.(map[string]*model.User) - } - - var preferences model.Preferences - - for id := range profiles { - if id == userId { - continue - } - - profile := profiles[id] - - preference := model.Preference{ - UserId: userId, - Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, - Name: profile.Id, - Value: "true", - } - - preferences = append(preferences, preference) - - if len(preferences) >= 10 { - break - } - } - - if result := <-Srv.Store.Preference().Save(&preferences); result.Err != nil { - l4g.Error("Failed to add direct channel preferences for user user_id=%s, eam_id=%s, err=%v", userId, teamId, result.Err.Error()) - return model.Preferences{} - } else { - return preferences - } -} - func getPreference(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) category := params["category"] diff --git a/api/preference_test.go b/api/preference_test.go index 318ce9582..dfdf8b581 100644 --- a/api/preference_test.go +++ b/api/preference_test.go @@ -113,54 +113,6 @@ func TestGetPreferenceCategory(t *testing.T) { } } -func TestTransformPreferences(t *testing.T) { - Setup() - - 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)) - - for i := 0; i < 5; i++ { - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} - Client.Must(Client.CreateUser(user, "")) - } - - Client.Must(Client.LoginByEmail(team.Name, user1.Email, "pwd")) - - if result, err := Client.GetPreferenceCategory(model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW); err != nil { - t.Fatal(err) - } else if data := result.Data.(model.Preferences); len(data) != 5 { - t.Fatal("received the wrong number of direct channels") - } - - 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)) - - for i := 0; i < 10; i++ { - user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"} - Client.Must(Client.CreateUser(user, "")) - } - - // make sure user1's preferences don't change - if result, err := Client.GetPreferenceCategory(model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW); err != nil { - t.Fatal(err) - } else if data := result.Data.(model.Preferences); len(data) != 5 { - t.Fatal("received the wrong number of direct channels") - } - - Client.Must(Client.LoginByEmail(team.Name, user2.Email, "pwd")) - - if result, err := Client.GetPreferenceCategory(model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW); err != nil { - t.Fatal(err) - } else if data := result.Data.(model.Preferences); len(data) != 10 { - t.Fatal("received the wrong number of direct channels") - } -} - func TestGetPreference(t *testing.T) { Setup() diff --git a/api/user.go b/api/user.go index 146ede015..1d628d301 100644 --- a/api/user.go +++ b/api/user.go @@ -237,6 +237,45 @@ func fireAndForgetWelcomeEmail(userId, email, teamName, teamDisplayName, siteURL }() } +func addDirectChannelsAndForget(user *model.User) { + go func() { + var profiles map[string]*model.User + if result := <-Srv.Store.User().GetProfiles(user.TeamId); result.Err != nil { + l4g.Error("Failed to add direct channel preferences for user user_id=%s, team_id=%s, err=%v", user.Id, user.TeamId, result.Err.Error()) + return + } else { + profiles = result.Data.(map[string]*model.User) + } + + var preferences model.Preferences + + for id := range profiles { + if id == user.Id { + continue + } + + profile := profiles[id] + + preference := model.Preference{ + UserId: user.Id, + Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, + Name: profile.Id, + Value: "true", + } + + preferences = append(preferences, preference) + + if len(preferences) >= 10 { + break + } + } + + if result := <-Srv.Store.Preference().Save(&preferences); result.Err != nil { + l4g.Error("Failed to add direct channel preferences for new user user_id=%s, eam_id=%s, err=%v", user.Id, user.TeamId, result.Err.Error()) + } + }() +} + func FireAndForgetVerifyEmail(userId, userEmail, teamName, teamDisplayName, siteURL, teamURL string) { go func() { -- cgit v1.2.3-1-g7c22 From 327b0b5a2119ae888c812f682b3934061b8f59bf Mon Sep 17 00:00:00 2001 From: hmhealey Date: Thu, 15 Oct 2015 15:09:40 -0400 Subject: Added an initial call to get all user preferences on page load --- api/preference.go | 11 +++++++ api/preference_test.go | 58 +++++++++++++++++++++++++++++++++++ model/client.go | 9 ++++++ store/sql_preference_store.go | 27 ++++++++++++++++ store/sql_preference_store_test.go | 48 +++++++++++++++++++++++++++++ store/store.go | 1 + web/react/components/sidebar.jsx | 6 +--- web/react/pages/channel.jsx | 3 ++ web/react/stores/preference_store.jsx | 1 + web/react/utils/async_client.jsx | 15 +++++---- web/react/utils/client.jsx | 13 ++++++++ 11 files changed, 179 insertions(+), 13 deletions(-) diff --git a/api/preference.go b/api/preference.go index 3a0535473..6d6ac1a7f 100644 --- a/api/preference.go +++ b/api/preference.go @@ -14,11 +14,22 @@ func InitPreference(r *mux.Router) { l4g.Debug("Initializing preference api routes") sr := r.PathPrefix("/preferences").Subrouter() + sr.Handle("/", ApiUserRequired(getAllPreferences)).Methods("GET") sr.Handle("/save", ApiUserRequired(savePreferences)).Methods("POST") sr.Handle("/{category:[A-Za-z0-9_]+}", ApiUserRequired(getPreferenceCategory)).Methods("GET") sr.Handle("/{category:[A-Za-z0-9_]+}/{name:[A-Za-z0-9_]+}", ApiUserRequired(getPreference)).Methods("GET") } +func getAllPreferences(c *Context, w http.ResponseWriter, r *http.Request) { + if result := <-Srv.Store.Preference().GetAll(c.Session.UserId); result.Err != nil { + c.Err = result.Err + } else { + data := result.Data.(model.Preferences) + + w.Write([]byte(data.ToJson())) + } +} + func savePreferences(c *Context, w http.ResponseWriter, r *http.Request) { preferences, err := model.PreferencesFromJson(r.Body) if err != nil { diff --git a/api/preference_test.go b/api/preference_test.go index dfdf8b581..9d3db9e2f 100644 --- a/api/preference_test.go +++ b/api/preference_test.go @@ -9,6 +9,64 @@ import ( "testing" ) +func TestGetAllPreferences(t *testing.T) { + Setup() + + 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)) + + 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)) + + category := model.NewId() + + preferences1 := model.Preferences{ + { + UserId: user1.Id, + Category: category, + Name: model.NewId(), + }, + { + UserId: user1.Id, + Category: category, + Name: model.NewId(), + }, + { + UserId: user1.Id, + Category: model.NewId(), + Name: model.NewId(), + }, + } + + Client.LoginByEmail(team.Name, user1.Email, "pwd") + Client.Must(Client.SetPreferences(&preferences1)) + + if result, err := Client.GetAllPreferences(); err != nil { + t.Fatal(err) + } else if data := result.Data.(model.Preferences); len(data) != 3 { + t.Fatal("received the wrong number of preferences") + } else if !((data[0] == preferences1[0] && data[1] == preferences1[1]) || (data[0] == preferences1[1] && data[1] == preferences1[0])) { + for i := 0; i < 3; i++ { + if data[0] != preferences1[i] && data[1] != preferences1[i] && data[2] != preferences1[i] { + t.Fatal("got incorrect preferences") + } + } + } + + Client.LoginByEmail(team.Name, user2.Email, "pwd") + + if result, err := Client.GetAllPreferences(); err != nil { + t.Fatal(err) + } else if data := result.Data.(model.Preferences); len(data) != 0 { + t.Fatal("received the wrong number of preferences") + } +} + func TestSetPreferences(t *testing.T) { Setup() diff --git a/model/client.go b/model/client.go index 77b0aaad2..ca1e37479 100644 --- a/model/client.go +++ b/model/client.go @@ -844,6 +844,15 @@ func (c *Client) ListIncomingWebhooks() (*Result, *AppError) { } } +func (c *Client) GetAllPreferences() (*Result, *AppError) { + if r, err := c.DoApiGet("/preferences/", "", ""); err != nil { + return nil, err + } else { + preferences, _ := PreferencesFromJson(r.Body) + return &Result{r.Header.Get(HEADER_REQUEST_ID), r.Header.Get(HEADER_ETAG_SERVER), preferences}, nil + } +} + func (c *Client) SetPreferences(preferences *Preferences) (*Result, *AppError) { if r, err := c.DoApiPost("/preferences/save", preferences.ToJson()); err != nil { return nil, err diff --git a/store/sql_preference_store.go b/store/sql_preference_store.go index 46cef38b1..7a0fe2f2e 100644 --- a/store/sql_preference_store.go +++ b/store/sql_preference_store.go @@ -212,3 +212,30 @@ func (s SqlPreferenceStore) GetCategory(userId string, category string) StoreCha return storeChannel } + +func (s SqlPreferenceStore) GetAll(userId string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + var preferences model.Preferences + + if _, err := s.GetReplica().Select(&preferences, + `SELECT + * + FROM + Preferences + WHERE + UserId = :UserId`, map[string]interface{}{"UserId": userId}); err != nil { + result.Err = model.NewAppError("SqlPreferenceStore.GetAll", "We encounted an error while finding preferences", err.Error()) + } else { + result.Data = preferences + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} diff --git a/store/sql_preference_store_test.go b/store/sql_preference_store_test.go index 76b1bcb17..e68203cc3 100644 --- a/store/sql_preference_store_test.go +++ b/store/sql_preference_store_test.go @@ -144,3 +144,51 @@ func TestPreferenceGetCategory(t *testing.T) { t.Fatal("shouldn't have got any preferences") } } + +func TestPreferenceGetAll(t *testing.T) { + Setup() + + userId := model.NewId() + category := model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW + name := model.NewId() + + preferences := model.Preferences{ + { + UserId: userId, + Category: category, + Name: name, + }, + // same user/category, different name + { + UserId: userId, + Category: category, + Name: model.NewId(), + }, + // same user/name, different category + { + UserId: userId, + Category: model.NewId(), + Name: name, + }, + // same name/category, different user + { + UserId: model.NewId(), + Category: category, + Name: name, + }, + } + + Must(store.Preference().Save(&preferences)) + + if result := <-store.Preference().GetAll(userId); result.Err != nil { + t.Fatal(result.Err) + } else if data := result.Data.(model.Preferences); len(data) != 3 { + t.Fatal("got the wrong number of preferences") + } else { + for i := 0; i < 3; i++ { + if data[0] != preferences[i] && data[1] != preferences[i] && data[2] != preferences[i] { + t.Fatal("got incorrect preferences") + } + } + } +} diff --git a/store/store.go b/store/store.go index b436a5c40..de335cc2b 100644 --- a/store/store.go +++ b/store/store.go @@ -156,4 +156,5 @@ type PreferenceStore interface { Save(preferences *model.Preferences) StoreChannel Get(userId string, category string, name string) StoreChannel GetCategory(userId string, category string) StoreChannel + GetAll(userId string) StoreChannel } diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx index e7d90bb88..5ab0c4f9e 100644 --- a/web/react/components/sidebar.jsx +++ b/web/react/components/sidebar.jsx @@ -132,11 +132,7 @@ export default class Sidebar extends React.Component { SocketStore.addChangeListener(this.onSocketChange); PreferenceStore.addChangeListener(this.onChange); - AsyncClient.getDirectChannelPreferences(); - - if ($(window).width() > 768) { - $('.nav-pills__container').perfectScrollbar(); - } + $('.nav-pills__container').perfectScrollbar(); this.updateTitle(); this.updateUnreadIndicators(); diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx index 698213eef..19e158aa6 100644 --- a/web/react/pages/channel.jsx +++ b/web/react/pages/channel.jsx @@ -37,6 +37,7 @@ var RegisterAppModal = require('../components/register_app_modal.jsx'); var ImportThemeModal = require('../components/user_settings/import_theme_modal.jsx'); var TeamStore = require('../stores/team_store.jsx'); +var AsyncClient = require('../utils/async_client.jsx'); var Constants = require('../utils/constants.jsx'); var ActionTypes = Constants.ActionTypes; @@ -54,6 +55,8 @@ function setupChannelPage(props) { id: props.TeamId }); + AsyncClient.getAllPreferences(); + // ChannelLoader must be rendered first React.render( , diff --git a/web/react/stores/preference_store.jsx b/web/react/stores/preference_store.jsx index d71efa10f..f630d150d 100644 --- a/web/react/stores/preference_store.jsx +++ b/web/react/stores/preference_store.jsx @@ -120,3 +120,4 @@ class PreferenceStoreClass extends EventEmitter { const PreferenceStore = new PreferenceStoreClass(); export default PreferenceStore; +window.PreferenceStore = PreferenceStore; diff --git a/web/react/utils/async_client.jsx b/web/react/utils/async_client.jsx index 1bf8a6fee..b22d7237e 100644 --- a/web/react/utils/async_client.jsx +++ b/web/react/utils/async_client.jsx @@ -638,16 +638,15 @@ export function getMyTeam() { ); } -export function getDirectChannelPreferences() { - if (isCallInProgress('getDirectChannelPreferences')) { +export function getAllPreferences() { + if (isCallInProgress('getAllPreferences')) { return; } - callTracker.getDirectChannelPreferences = utils.getTimestamp(); - client.getPreferenceCategory( - Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, + callTracker.getAllPreferences = utils.getTimestamp(); + client.getAllPreferences( (data, textStatus, xhr) => { - callTracker.getDirectChannelPreferences = 0; + callTracker.getAllPreferences = 0; if (xhr.status === 304 || !data) { return; @@ -659,8 +658,8 @@ export function getDirectChannelPreferences() { }); }, (err) => { - callTracker.getDirectChannelPreferences = 0; - dispatchError(err, 'getDirectChannelPreferences'); + callTracker.getAllPreferences = 0; + dispatchError(err, 'getAllPreferences'); } ); } diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx index 76a402855..f6aee362c 100644 --- a/web/react/utils/client.jsx +++ b/web/react/utils/client.jsx @@ -1142,6 +1142,19 @@ export function listIncomingHooks(success, error) { }); } +export function getAllPreferences(success, error) { + $.ajax({ + url: `/api/v1/preferences/`, + dataType: 'json', + type: 'GET', + success, + error: (xhr, status, err) => { + var e = handleError('getAllPreferences', xhr, status, err); + error(e); + } + }); +} + export function getPreferenceCategory(category, success, error) { $.ajax({ url: `/api/v1/preferences/${category}`, -- cgit v1.2.3-1-g7c22 From 218af22936f55358f3b31b618e41004c0e2142a3 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Thu, 15 Oct 2015 15:39:50 -0400 Subject: Changed preference saving to use the master database --- store/sql_preference_store.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/store/sql_preference_store.go b/store/sql_preference_store.go index 46cef38b1..dbbe20119 100644 --- a/store/sql_preference_store.go +++ b/store/sql_preference_store.go @@ -43,7 +43,7 @@ func (s SqlPreferenceStore) Save(preferences *model.Preferences) StoreChannel { result := StoreResult{} // wrap in a transaction so that if one fails, everything fails - transaction, err := s.GetReplica().Begin() + transaction, err := s.GetMaster().Begin() if err != nil { result.Err = model.NewAppError("SqlPreferenceStore.Save", "Unable to open transaction to save preferences", err.Error()) } else { -- cgit v1.2.3-1-g7c22 From 50a8e2bd4fa53c6bb2f12a03237be4fbaf259617 Mon Sep 17 00:00:00 2001 From: it33 Date: Thu, 15 Oct 2015 23:18:08 -0700 Subject: Update Troubleshooting.md --- doc/install/Troubleshooting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/install/Troubleshooting.md b/doc/install/Troubleshooting.md index 8d82100d8..b87663ab3 100644 --- a/doc/install/Troubleshooting.md +++ b/doc/install/Troubleshooting.md @@ -3,5 +3,5 @@ #### Important notes 1. **DO NOT manipulate the Mattermost database** - - In particular, DO NOT delete data from the database, as this will most likely crash Mattermost in strange ways. Mattermost is designed to archive content continously and generally assumes data is never deleted. + - In particular, DO NOT delete data from the database, as Mattermost is designed to stop working if data integrity has been compromised. The system is designed to archive content continously and generally assumes data is never deleted. -- cgit v1.2.3-1-g7c22