diff --git a/README.md b/README.md index 80f0f4c38c..c4276ed78a 100644 --- a/README.md +++ b/README.md @@ -79,18 +79,26 @@ Local Machine Setup (Docker) 3. When docker is done fetching the image, open http://localhost:8065/ in your browser. ### Additional Notes ### -- If you want to work with the latest bits in the repository (i.e. not a stable release) you can run the cmd: -`docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform:dev` +- If you want to work with the latest master from the repository (i.e. not a stable release) you can run the cmd: -- You can update to the latest bits by running: -`docker pull mattermost/platform:dev` + ``` bash + docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform:dev + ``` + +- Instructions on how to update your docker image are found below. - If you wish to remove mattermost-dev use: - `docker stop mattermost-dev` - `docker rm -v mattermost-dev` + + ``` bash + docker stop mattermost-dev + docker rm -v mattermost-dev + ``` - If you wish to gain access to a shell on the container use: - `docker exec -ti mattermost-dev /bin/bash` + + ``` bash + docker exec -ti mattermost-dev /bin/bash + ``` AWS Elastic Beanstalk Setup (Docker) ------------------------------------ @@ -163,6 +171,35 @@ Email Setup (Optional) 3. The service should restart automatically. Verify the Mattermost service is running with `ps -A` 4. Current logged in users will not be affected, but upon logging out or session expiration users will be required to verify their email address. +Upgrading Mattermost +--------------------- + +### Docker ### +To upgrade your docker image to the latest release (NOTE: this will destroy all data in the docker container): + +1. Stop your docker container by running: + + ``` bash + docker stop mattermost-dev + ``` +2. Delete your docker container by running: + + ``` bash + docker rm mattermost-dev + ``` +3. Update your docker image by running: + + ``` bash + docker pull mattermost/platform + ``` +4. Start your docker container by running: + + ``` bash + docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform + ``` + +To upgrade to the latest master from the repository replace `mattermost/platform` with `mattermost/platform:dev` in the above instructions. + Contributing ------------ diff --git a/store/sql_user_store.go b/store/sql_user_store.go index cd25b488b0..4b1189c2ee 100644 --- a/store/sql_user_store.go +++ b/store/sql_user_store.go @@ -5,6 +5,7 @@ package store import ( "fmt" + "strings" "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" ) @@ -163,6 +164,17 @@ func (us SqlUserStore) Update(user *model.User, allowActiveUpdate bool) StoreCha user.EmailVerified = false } + if user.Username != oldUser.Username { + nonUsernameKeys := []string{} + splitKeys := strings.Split(user.NotifyProps["mention_keys"], ",") + for _, key := range splitKeys { + if key != oldUser.Username && key != "@" + oldUser.Username { + nonUsernameKeys = append(nonUsernameKeys, key) + } + } + user.NotifyProps["mention_keys"] = strings.Join(nonUsernameKeys, ",") + user.Username + ",@" + user.Username + } + if count, err := us.GetMaster().Update(user); err != nil { if IsUniqueConstraintError(err.Error(), "Email", "users_email_teamid_key") { result.Err = model.NewAppError("SqlUserStore.Update", "This email is already taken. Please choose another", "user_id="+user.Id+", "+err.Error()) diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx index 90a7767917..0254d0e82e 100644 --- a/web/react/components/channel_header.jsx +++ b/web/react/components/channel_header.jsx @@ -105,7 +105,7 @@ module.exports = React.createClass({ if (!utils.areStatesEqual(newState, this.state)) { this.setState(newState); } - $('.channel-header__info .description').popover({placement: 'bottom', trigger: 'hover', html: true, delay: {show: 500, hide: 500}}); + $('.channel-header__info .description').popover({placement: 'bottom', trigger: 'hover click', html: true, delay: {show: 500, hide: 500}}); }, onSocketChange: function(msg) { if (msg.action === 'new_user') { diff --git a/web/react/components/channel_info_modal.jsx b/web/react/components/channel_info_modal.jsx index 18addb52fe..6d999870aa 100644 --- a/web/react/components/channel_info_modal.jsx +++ b/web/react/components/channel_info_modal.jsx @@ -32,7 +32,7 @@ module.exports = React.createClass({
-

{channel.display_name}

+

{channel.display_name}

diff --git a/web/react/components/channel_invite_modal.jsx b/web/react/components/channel_invite_modal.jsx index d90522e8ce..e446167ec3 100644 --- a/web/react/components/channel_invite_modal.jsx +++ b/web/react/components/channel_invite_modal.jsx @@ -138,7 +138,7 @@ export default class ChannelInviteModal extends React.Component {
-

Add New Members to {this.state.channelName}

+

Add New Members to {this.state.channel_name}

{inviteError} diff --git a/web/react/components/channel_members.jsx b/web/react/components/channel_members.jsx index cfb8ed41c1..db4bec4003 100644 --- a/web/react/components/channel_members.jsx +++ b/web/react/components/channel_members.jsx @@ -126,7 +126,7 @@ module.exports = React.createClass({
-

{this.state.channel_name + " Members"}

+

{this.state.channel_name} Members

Add New Members
diff --git a/web/react/components/channel_notifications.jsx b/web/react/components/channel_notifications.jsx index 38bc916825..884bad9d2d 100644 --- a/web/react/components/channel_notifications.jsx +++ b/web/react/components/channel_notifications.jsx @@ -9,159 +9,230 @@ var client = require('../utils/client.jsx'); var UserStore = require('../stores/user_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx'); -module.exports = React.createClass({ - componentDidMount: function() { - ChannelStore.addChangeListener(this._onChange); +export default class ChannelNotifications extends React.Component { + constructor(props) { + super(props); + this.onListenerChange = this.onListenerChange.bind(this); + this.updateSection = this.updateSection.bind(this); + this.handleUpdate = this.handleUpdate.bind(this); + this.handleRadioClick = this.handleRadioClick.bind(this); + this.handleQuietToggle = this.handleQuietToggle.bind(this); + this.state = {notifyLevel: '', title: '', channelId: '', activeSection: ''}; + } + componentDidMount() { + ChannelStore.addChangeListener(this.onListenerChange); var self = this; - $(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) { + $(this.refs.modal.getDOMNode()).on('show.bs.modal', function showModal(e) { var button = e.relatedTarget; - var channel_id = button.getAttribute('data-channelid'); + var channelId = button.getAttribute('data-channelid'); - var notifyLevel = ChannelStore.getMember(channel_id).notify_level; + var notifyLevel = ChannelStore.getMember(channelId).notify_level; var quietMode = false; - if (notifyLevel === "quiet") quietMode = true; - self.setState({ notify_level: notifyLevel, quiet_mode: quietMode, title: button.getAttribute('data-title'), channel_id: channel_id }); + + if (notifyLevel === 'quiet') { + quietMode = true; + } + + self.setState({notifyLevel: notifyLevel, quietMode: quietMode, title: button.getAttribute('data-title'), channelId: channelId}); }); - }, - componentWillUnmount: function() { - ChannelStore.removeChangeListener(this._onChange); - }, - _onChange: function() { - if (!this.state.channel_id) return; - var notifyLevel = ChannelStore.getMember(this.state.channel_id).notify_level; + } + componentWillUnmount() { + ChannelStore.removeChangeListener(this.onListenerChange); + } + onListenerChange() { + if (!this.state.channelId) { + return; + } + + var notifyLevel = ChannelStore.getMember(this.state.channelId).notify_level; var quietMode = false; - if (notifyLevel === "quiet") quietMode = true; + if (notifyLevel === 'quiet') { + quietMode = true; + } var newState = this.state; - newState.notify_level = notifyLevel; - newState.quiet_mode = quietMode; + newState.notifyLevel = notifyLevel; + newState.quietMode = quietMode; if (!utils.areStatesEqual(this.state, newState)) { this.setState(newState); } - }, - updateSection: function(section) { - this.setState({ activeSection: section }); - }, - getInitialState: function() { - return { notify_level: "", title: "", channel_id: "", activeSection: "" }; - }, - handleUpdate: function() { - var channel_id = this.state.channel_id; - var notify_level = this.state.quiet_mode ? "quiet" : this.state.notify_level; + } + updateSection(section) { + this.setState({activeSection: section}); + } + handleUpdate() { + var channelId = this.state.channelId; + var notifyLevel = this.state.notifyLevel; + if (this.state.quietMode) { + notifyLevel = 'quiet'; + } var data = {}; - data["channel_id"] = channel_id; - data["user_id"] = UserStore.getCurrentId(); - data["notify_level"] = notify_level; + data.channel_id = channelId; + data.user_id = UserStore.getCurrentId(); + data.notify_level = notifyLevel; - if (!data["notify_level"] || data["notify_level"].length === 0) return; + if (!data.notify_level || data.notify_level.length === 0) { + return; + } client.updateNotifyLevel(data, - function(data) { - var member = ChannelStore.getMember(channel_id); - member.notify_level = notify_level; + function success() { + var member = ChannelStore.getMember(channelId); + member.notify_level = notifyLevel; ChannelStore.setChannelMember(member); - this.updateSection(""); + this.updateSection(''); }.bind(this), - function(err) { - this.setState({ server_error: err.message }); + function error(err) { + this.setState({serverError: err.message}); }.bind(this) ); - }, - handleRadioClick: function(notifyLevel) { - this.setState({ notify_level: notifyLevel, quiet_mode: false }); + } + handleRadioClick(notifyLevel) { + this.setState({notifyLevel: notifyLevel, quietMode: false}); this.refs.modal.getDOMNode().focus(); - }, - handleQuietToggle: function(quietMode) { - this.setState({ notify_level: "none", quiet_mode: quietMode }); + } + handleQuietToggle(quietMode) { + this.setState({notifyLevel: 'none', quietMode: quietMode}); this.refs.modal.getDOMNode().focus(); - }, - render: function() { - var server_error = this.state.server_error ?
: null; + } + render() { + var serverError = null; + if (this.state.serverError) { + serverError =
; + } var self = this; + var describe = ''; + var inputs = []; + + var handleUpdateSection; var desktopSection; if (this.state.activeSection === 'desktop') { var notifyActive = [false, false, false]; - if (this.state.notify_level === "mention") { + if (this.state.notifyLevel === 'mention') { notifyActive[1] = true; - } else if (this.state.notify_level === "all") { + } else if (this.state.notifyLevel === 'all') { notifyActive[0] = true; } else { notifyActive[2] = true; } - var inputs = []; - inputs.push(
-
+

-
+

-
+
); + handleUpdateSection = function updateSection(e) { + self.updateSection(''); + self.onListenerChange(); + e.preventDefault(); + }; + desktopSection = ( ); } else { - var describe = ""; - if (this.state.notify_level === "mention") { - describe = "Only for mentions"; - } else if (this.state.notify_level === "all") { - describe = "For all activity"; + if (this.state.notifyLevel === 'mention') { + describe = 'Only for mentions'; + } else if (this.state.notifyLevel === 'all') { + describe = 'For all activity'; } else { - describe = "Never"; + describe = 'Never'; } + handleUpdateSection = function updateSection(e) { + self.updateSection('desktop'); + e.preventDefault(); + }; + desktopSection = ( ); } var quietSection; if (this.state.activeSection === 'quiet') { - var quietActive = ["",""]; - if (this.state.quiet_mode) { - quietActive[0] = "active"; + var quietActive = [false, false]; + if (this.state.quietMode) { + quietActive[0] = true; } else { - quietActive[1] = "active"; + quietActive[1] = true; } - var inputs = []; - inputs.push(
-
- - +
+ +
+
+
+ +
); @@ -173,62 +244,85 @@ module.exports = React.createClass({
); + handleUpdateSection = function updateSection(e) { + self.updateSection(''); + self.onListenerChange(); + e.preventDefault(); + }; + quietSection = ( ); } else { - var describe = ""; - if (this.state.quiet_mode) { - describe = "On"; + if (this.state.quietMode) { + describe = 'On'; } else { - describe = "Off"; + describe = 'Off'; } + handleUpdateSection = function updateSection(e) { + self.updateSection('quiet'); + e.preventDefault(); + }; + quietSection = ( ); } - var self = this; return ( -