summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorReed Garmsen <rgarmsen2295@gmail.com>2015-06-19 11:49:59 -0700
committerReed Garmsen <rgarmsen2295@gmail.com>2015-06-19 11:49:59 -0700
commit9f652310a5ee9c51633264d08434e4a08224a9eb (patch)
tree80e91b8343f567f301d247392d09ba620909076d
parentc1740ffa2fcfcf663d8e847cfe03187fb600b6c1 (diff)
parent6c8d7b1b9fe00e60582900b7c1e782e2c0a00325 (diff)
downloadchat-9f652310a5ee9c51633264d08434e4a08224a9eb.tar.gz
chat-9f652310a5ee9c51633264d08434e4a08224a9eb.tar.bz2
chat-9f652310a5ee9c51633264d08434e4a08224a9eb.zip
Merge pull request #16 from rgarmsen2295/master
Remerging
-rw-r--r--README.md13
-rw-r--r--api/post.go2
-rw-r--r--api/user.go68
-rw-r--r--api/user_test.go15
-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
7 files changed, 177 insertions, 74 deletions
diff --git a/README.md b/README.md
index 53259ea10..a0aeb974c 100644
--- a/README.md
+++ b/README.md
@@ -34,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/api/user.go b/api/user.go
index 83e29b28e..6af737df3 100644
--- a/api/user.go
+++ b/api/user.go
@@ -567,7 +567,7 @@ func getAudits(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
-func createProfileImage(username string, userId string) *image.RGBA {
+func createProfileImage(username string, userId string) ([]byte, *model.AppError) {
colors := []color.NRGBA{
{197, 8, 126, 255},
@@ -634,16 +634,17 @@ func createProfileImage(username string, userId string) *image.RGBA {
gc.Translate(width, height)
gc.SetFillColor(image.White)
gc.FillString(initials)
- return i
-}
-func getProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
- if !utils.IsS3Configured() {
- c.Err = model.NewAppError("getProfileImage", "Unable to get image. Amazon S3 not configured. ", "")
- c.Err.StatusCode = http.StatusNotImplemented
- return
+ buf := new(bytes.Buffer)
+
+ if imgErr := png.Encode(buf, i); imgErr != nil {
+ return nil, model.NewAppError("getProfileImage", "Could not encode default profile image", imgErr.Error())
+ } else {
+ return buf.Bytes(), nil
}
+}
+func getProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id := params["id"]
@@ -651,36 +652,41 @@ func getProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = result.Err
return
} else {
- var auth aws.Auth
- auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId
- auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey
+ var img []byte
+ var err *model.AppError
+
+ if !utils.IsS3Configured() {
+ img, err = createProfileImage(result.Data.(*model.User).Username, id)
+ if err != nil {
+ c.Err = err
+ return
+ }
+ } else {
+ var auth aws.Auth
+ auth.AccessKey = utils.Cfg.AWSSettings.S3AccessKeyId
+ auth.SecretKey = utils.Cfg.AWSSettings.S3SecretAccessKey
- s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region])
- bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket)
+ s := s3.New(auth, aws.Regions[utils.Cfg.AWSSettings.S3Region])
+ bucket := s.Bucket(utils.Cfg.AWSSettings.S3Bucket)
- path := "teams/" + c.Session.TeamId + "/users/" + id + "/profile.png"
+ path := "teams/" + c.Session.TeamId + "/users/" + id + "/profile.png"
- var img []byte
+ if data, getErr := bucket.Get(path); getErr != nil {
+ img, err = createProfileImage(result.Data.(*model.User).Username, id)
+ if err != nil {
+ c.Err = err
+ return
+ }
- if data, getErr := bucket.Get(path); getErr != nil {
- rawImg := createProfileImage(result.Data.(*model.User).Username, id)
- buf := new(bytes.Buffer)
+ options := s3.Options{}
+ if err := bucket.Put(path, img, "image", s3.Private, options); err != nil {
+ c.Err = model.NewAppError("getImage", "Couldn't upload default profile image", err.Error())
+ return
+ }
- if imgErr := png.Encode(buf, rawImg); imgErr != nil {
- c.Err = model.NewAppError("getProfileImage", "Could not encode default profile image", imgErr.Error())
- return
} else {
- img = buf.Bytes()
- }
-
- options := s3.Options{}
- if err := bucket.Put(path, buf.Bytes(), "image", s3.Private, options); err != nil {
- c.Err = model.NewAppError("getImage", "Couldn't upload default profile image", err.Error())
- return
+ img = data
}
-
- } else {
- img = data
}
if c.Session.UserId == id {
diff --git a/api/user_test.go b/api/user_test.go
index 4d5d2b3f0..92ab216aa 100644
--- a/api/user_test.go
+++ b/api/user_test.go
@@ -10,6 +10,7 @@ import (
"github.com/goamz/goamz/s3"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
+ "image"
"image/color"
"io"
"mime/multipart"
@@ -324,14 +325,20 @@ func TestGetAudits(t *testing.T) {
func TestUserCreateImage(t *testing.T) {
Setup()
- i := createProfileImage("Corey Hulen", "eo1zkdr96pdj98pjmq8zy35wba")
- if i == nil {
- t.Fatal("Failed to gen image")
+ b, err := createProfileImage("Corey Hulen", "eo1zkdr96pdj98pjmq8zy35wba")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ rdr := bytes.NewReader(b)
+ img, _, err2 := image.Decode(rdr)
+ if err2 != nil {
+ t.Fatal(err)
}
colorful := color.RGBA{116, 49, 196, 255}
- if i.RGBAAt(1, 1) != colorful {
+ if img.At(1, 1) != colorful {
t.Fatal("Failed to create correct color")
}
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 {