summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md24
-rw-r--r--api/post.go2
-rw-r--r--model/utils.go2
-rw-r--r--web/react/components/confirm_modal.jsx31
-rw-r--r--web/react/components/invite_member_modal.jsx120
5 files changed, 131 insertions, 48 deletions
diff --git a/README.md b/README.md
index 6bcb0fcfc..a0aeb974c 100644
--- a/README.md
+++ b/README.md
@@ -6,16 +6,9 @@
About Mattermost
================
-Mattermost is an open source team communication service. It brings messaging and files shared by your team into one place accessible across PCs and phones, with archiving and search.
+Mattermost is a team communication service. It brings team messaging and file sharing into one place, accessible across PCs and phones, with archiving and search.
-<br>
-WHAT MATTERS MOST TO YOUR TEAM?
-
-Words have power.
-Mattermost serves teams who use words to shape the future.
-The words you choose are up to you.
-
-*- SpinPunch*
+We built Mattermost to help teams focus on what matters most to them. It works for us, we hope it works for you too.
Installing the Mattermost Preview
@@ -41,22 +34,35 @@ Local Machine Setup (Docker)
### Ubuntu ###
1. Follow the instructions at https://docs.docker.com/installation/ubuntulinux/ or use the summery below.
+
`sudo apt-get update`
+
`sudo apt-get install wget`
+
`wget -qO- https://get.docker.com/ | sh`
+
`sudo usermod -aG docker <username>`
+
`sudo service docker start`
+
`newgrp docker`
+
2. Run `docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform:helium
3. When docker is done fetching the image, open http://localhost:8065/ in your browser
### Arch ###
1. Install docker using the following commands
+
`pacman -S docker`
+
`systemctl enable docker.service`
+
`systemctl start docker.service`
+
`gpasswd -a <username> docker`
+
`newgrp docker`
+
2. docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform:helium
3. When docker is done fetching the image, open http://localhost:8065/ in your browser
diff --git a/api/post.go b/api/post.go
index 36607c231..3acc95551 100644
--- a/api/post.go
+++ b/api/post.go
@@ -302,7 +302,7 @@ func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) {
// Build a map as a list of unique user_ids that are mentioned in this post
splitF := func(c rune) bool {
- return c == ',' || c == ' ' || c == '.' || c == '!' || c == '?' || c == ':' || c == '<' || c == '>'
+ return model.SplitRunes[c]
}
splitMessage := strings.FieldsFunc(strings.Replace(post.Message, "<br>", " ", -1), splitF)
for _, word := range splitMessage {
diff --git a/model/utils.go b/model/utils.go
index 2541247de..262bda319 100644
--- a/model/utils.go
+++ b/model/utils.go
@@ -319,3 +319,5 @@ func ClearMentionTags(post string) string {
var UrlRegex = regexp.MustCompile(`^((?:[a-z]+:\/\/)?(?:(?:[a-z0-9\-]+\.)+(?:[a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(?:\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(?:\?[a-z0-9+_~\-\.%=&amp;]*)?)?(?:#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(?:\s+|$)$`)
var PartialUrlRegex = regexp.MustCompile(`/api/v1/files/(get|get_image)/([A-Za-z0-9]{26})/([A-Za-z0-9]{26})/(([A-Za-z0-9]+/)?.+\.[A-Za-z0-9]{3,})`)
+
+var SplitRunes = map[rune]bool{',': true, ' ': true, '.': true, '!': true, '?': true, ':': true, ';': true, '\n': true, '<': true, '>': true, '(': true, ')': true, '{': true, '}': true, '[': true, ']': true, '+': true, '/': true, '\\': true}
diff --git a/web/react/components/confirm_modal.jsx b/web/react/components/confirm_modal.jsx
new file mode 100644
index 000000000..3be13cf9b
--- /dev/null
+++ b/web/react/components/confirm_modal.jsx
@@ -0,0 +1,31 @@
+// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+module.exports = React.createClass({
+ handleConfirm: function() {
+ $('#'+this.props.parent_id).attr('data-confirm', 'true');
+ $('#'+this.props.parent_id).modal('hide');
+ $('#'+this.props.id).modal('hide');
+ },
+ render: function() {
+ return (
+ <div className="modal fade" id={this.props.id} tabIndex="-1" role="dialog" aria-hidden="true">
+ <div className="modal-dialog">
+ <div className="modal-content">
+ <div className="modal-header">
+ <h4 className="modal-title">{this.props.title}</h4>
+ </div>
+ <div className="modal-body">
+ {this.props.message}
+ </div>
+ <div className="modal-footer">
+ <button type="button" className="btn btn-default" data-dismiss="modal">Cancel</button>
+ <button onClick={this.handleConfirm} type="button" className="btn btn-primary">{this.props.confirm_button}</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+});
+
diff --git a/web/react/components/invite_member_modal.jsx b/web/react/components/invite_member_modal.jsx
index 1d2bbed84..5980664de 100644
--- a/web/react/components/invite_member_modal.jsx
+++ b/web/react/components/invite_member_modal.jsx
@@ -4,8 +4,37 @@
var utils = require('../utils/utils.jsx');
var Client =require('../utils/client.jsx');
var UserStore = require('../stores/user_store.jsx');
+var ConfirmModal = require('./confirm_modal.jsx');
module.exports = React.createClass({
+ componentDidMount: function() {
+ var self = this;
+ $('#invite_member').on('hide.bs.modal', function(e) {
+ if ($('#invite_member').attr('data-confirm') === 'true') {
+ $('#invite_member').attr('data-confirm', 'false');
+ return;
+ }
+
+ var not_empty = false;
+ for (var i = 0; i < self.state.invite_ids.length; i++) {
+ var index = self.state.invite_ids[i];
+ if (self.refs["email"+index].getDOMNode().value.trim() !== '') {
+ not_empty = true;
+ break;
+ }
+ }
+
+ if (not_empty) {
+ $('#confirm_invite_modal').modal('show');
+ e.preventDefault();
+ }
+
+ });
+
+ $('#invite_member').on('hidden.bs.modal', function() {
+ self.clearFields();
+ });
+ },
handleSubmit: function(e) {
var invite_ids = this.state.invite_ids;
var count = invite_ids.length;
@@ -56,22 +85,8 @@ module.exports = React.createClass({
Client.inviteMembers(data,
function() {
+ $(this.refs.modal.getDOMNode()).attr('data-confirm', 'true');
$(this.refs.modal.getDOMNode()).modal('hide');
- for (var i = 0; i < invite_ids.length; i++) {
- var index = invite_ids[i];
- this.refs["email"+index].getDOMNode().value = "";
- if (config.AllowInviteNames) {
- this.refs["first_name"+index].getDOMNode().value = "";
- this.refs["last_name"+index].getDOMNode().value = "";
- }
- }
- this.setState({
- invite_ids: [0],
- id_count: 0,
- email_errors: {},
- first_name_errors: {},
- last_name_errors: {}
- });
}.bind(this),
function(err) {
this.setState({ server_error: err });
@@ -89,6 +104,26 @@ module.exports = React.createClass({
invite_ids.push(count);
this.setState({ invite_ids: invite_ids, id_count: count });
},
+ clearFields: function() {
+ var invite_ids = this.state.invite_ids;
+
+ for (var i = 0; i < invite_ids.length; i++) {
+ var index = invite_ids[i];
+ this.refs["email"+index].getDOMNode().value = "";
+ if (config.AllowInviteNames) {
+ this.refs["first_name"+index].getDOMNode().value = "";
+ this.refs["last_name"+index].getDOMNode().value = "";
+ }
+ }
+
+ this.setState({
+ invite_ids: [0],
+ id_count: 0,
+ email_errors: {},
+ first_name_errors: {},
+ last_name_errors: {}
+ });
+ },
removeInviteFields: function(index) {
var invite_ids = this.state.invite_ids;
var i = invite_ids.indexOf(index);
@@ -147,29 +182,38 @@ module.exports = React.createClass({
var server_error = this.state.server_error ? <div className='form-group has-error'><label className='control-label'>{ this.state.server_error }</label></div> : null;
return (
- <div className="modal fade" ref="modal" id="invite_member" tabIndex="-1" role="dialog" aria-hidden="true">
- <div className="modal-dialog">
- <div className="modal-content">
- <div className="modal-header">
- <button type="button" className="close" data-dismiss="modal" aria-label="Close" data-reactid=".5.0.0.0.0"><span aria-hidden="true" data-reactid=".5.0.0.0.0.0">×</span></button>
- <h4 className="modal-title" id="myModalLabel">Invite New Member</h4>
- </div>
- <div ref="modalBody" className="modal-body">
- <form role="form">
- { invite_sections }
- </form>
- { server_error }
- <button type="button" className="btn btn-default" onClick={this.addInviteFields}>Add another</button>
- <br/>
- <br/>
- <label className='control-label'>People invited automatically join Town Square channel.</label>
- </div>
- <div className="modal-footer">
- <button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
- <button onClick={this.handleSubmit} type="button" className="btn btn-primary">Send Invitations</button>
- </div>
- </div>
- </div>
+ <div>
+ <div className="modal fade" ref="modal" id="invite_member" tabIndex="-1" role="dialog" aria-hidden="true">
+ <div className="modal-dialog">
+ <div className="modal-content">
+ <div className="modal-header">
+ <button type="button" className="close" data-dismiss="modal" aria-label="Close" data-reactid=".5.0.0.0.0"><span aria-hidden="true" data-reactid=".5.0.0.0.0.0">×</span></button>
+ <h4 className="modal-title" id="myModalLabel">Invite New Member</h4>
+ </div>
+ <div ref="modalBody" className="modal-body">
+ <form role="form">
+ { invite_sections }
+ </form>
+ { server_error }
+ <button type="button" className="btn btn-default" onClick={this.addInviteFields}>Add another</button>
+ <br/>
+ <br/>
+ <label className='control-label'>People invited automatically join Town Square channel.</label>
+ </div>
+ <div className="modal-footer">
+ <button type="button" className="btn btn-default" data-dismiss="modal">Close</button>
+ <button onClick={this.handleSubmit} type="button" className="btn btn-primary">Send Invitations</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ <ConfirmModal
+ id="confirm_invite_modal"
+ parent_id="invite_member"
+ title="Discard Invitations?"
+ message="You have unsent invitations, are you sure you want to discard them?"
+ confirm_button="Yes, Discard"
+ />
</div>
);
} else {