summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--License.txt5
-rw-r--r--NOTICE.txt4
-rw-r--r--api/command.go6
-rw-r--r--api/file.go2
-rw-r--r--api/post.go6
-rw-r--r--api/post_test.go98
-rw-r--r--api/team.go8
-rw-r--r--api/user.go4
-rw-r--r--config/config.json1
-rw-r--r--utils/config.go1
-rw-r--r--web/react/components/create_comment.jsx7
-rw-r--r--web/react/components/find_team.jsx2
-rw-r--r--web/react/components/login.jsx2
-rw-r--r--web/react/components/post_deleted_modal.jsx2
-rw-r--r--web/react/components/settings_sidebar.jsx6
-rw-r--r--web/react/components/user_settings.jsx5
-rw-r--r--web/react/components/view_image.jsx2
-rw-r--r--web/react/utils/client.jsx4
-rw-r--r--web/templates/head.html33
19 files changed, 121 insertions, 77 deletions
diff --git a/License.txt b/License.txt
deleted file mode 100644
index 6abd425dd..000000000
--- a/License.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-XXXXXX - TODO
-
-someone needs to update \ No newline at end of file
diff --git a/NOTICE.txt b/NOTICE.txt
new file mode 100644
index 000000000..07c6a8bf4
--- /dev/null
+++ b/NOTICE.txt
@@ -0,0 +1,4 @@
+Mattermost Platform Preview
+Copyright 2015 SpinPunch, Inc.
+
+Regular expression support is provided by the PCRE library package, which is open source software, written by Philip Hazel, and copyright by the University of Cambridge, England. The original software is available from ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/
diff --git a/api/command.go b/api/command.go
index 9efc79b49..449483bbf 100644
--- a/api/command.go
+++ b/api/command.go
@@ -19,12 +19,16 @@ var commands = []commandHandler{
logoutCommand,
joinCommand,
loadTestCommand,
- echoCommand,
}
func InitCommand(r *mux.Router) {
l4g.Debug("Initializing command api routes")
r.Handle("/command", ApiUserRequired(command)).Methods("POST")
+
+ if utils.Cfg.TeamSettings.AllowValet {
+ commands = append(commands, echoCommand)
+ }
+
hub.Start()
}
diff --git a/api/file.go b/api/file.go
index c7c3b7b3e..10167c6ff 100644
--- a/api/file.go
+++ b/api/file.go
@@ -114,7 +114,7 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- fileUrl := c.TeamUrl + "/api/v1/files/get/" + channelId + "/" + c.Session.UserId + "/" + uid + "/" + files[i].Filename
+ fileUrl := c.TeamUrl + "/api/v1/files/get/" + channelId + "/" + c.Session.UserId + "/" + uid + "/" + url.QueryEscape(files[i].Filename)
resStruct.Filenames = append(resStruct.Filenames, fileUrl)
}
diff --git a/api/post.go b/api/post.go
index 25a68304d..36607c231 100644
--- a/api/post.go
+++ b/api/post.go
@@ -58,6 +58,12 @@ func createPost(c *Context, w http.ResponseWriter, r *http.Request) {
}
func createValetPost(c *Context, w http.ResponseWriter, r *http.Request) {
+ if !utils.Cfg.TeamSettings.AllowValet {
+ c.Err = model.NewAppError("createValetPost", "The valet feature is currently turned off. Please contact your system administrator for details.", "")
+ c.Err.StatusCode = http.StatusNotImplemented
+ return
+ }
+
post := model.PostFromJson(r.Body)
if post == nil {
c.SetInvalidParam("createValetPost", "post")
diff --git a/api/post_test.go b/api/post_test.go
index 4b40bc06a..b322a5017 100644
--- a/api/post_test.go
+++ b/api/post_test.go
@@ -147,62 +147,70 @@ func TestCreateValetPost(t *testing.T) {
channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
- post1 := &model.Post{ChannelId: channel1.Id, Message: "#hashtag a" + model.NewId() + "a"}
- rpost1, err := Client.CreateValetPost(post1)
- if err != nil {
- t.Fatal(err)
- }
+ if utils.Cfg.TeamSettings.AllowValet {
+ post1 := &model.Post{ChannelId: channel1.Id, Message: "#hashtag a" + model.NewId() + "a"}
+ rpost1, err := Client.CreateValetPost(post1)
+ if err != nil {
+ t.Fatal(err)
+ }
- if rpost1.Data.(*model.Post).Message != post1.Message {
- t.Fatal("message didn't match")
- }
+ if rpost1.Data.(*model.Post).Message != post1.Message {
+ t.Fatal("message didn't match")
+ }
- if rpost1.Data.(*model.Post).Hashtags != "#hashtag" {
- t.Fatal("hashtag didn't match")
- }
+ if rpost1.Data.(*model.Post).Hashtags != "#hashtag" {
+ t.Fatal("hashtag didn't match")
+ }
- post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id}
- rpost2, err := Client.CreateValetPost(post2)
- if err != nil {
- t.Fatal(err)
- }
+ post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id}
+ rpost2, err := Client.CreateValetPost(post2)
+ if err != nil {
+ t.Fatal(err)
+ }
- post3 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id, ParentId: rpost2.Data.(*model.Post).Id}
- _, err = Client.CreateValetPost(post3)
- if err != nil {
- t.Fatal(err)
- }
+ post3 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id, ParentId: rpost2.Data.(*model.Post).Id}
+ _, err = Client.CreateValetPost(post3)
+ if err != nil {
+ t.Fatal(err)
+ }
- post4 := &model.Post{ChannelId: "junk", Message: "a" + model.NewId() + "a"}
- _, err = Client.CreateValetPost(post4)
- if err.StatusCode != http.StatusForbidden {
- t.Fatal("Should have been forbidden")
- }
+ post4 := &model.Post{ChannelId: "junk", Message: "a" + model.NewId() + "a"}
+ _, err = Client.CreateValetPost(post4)
+ if err.StatusCode != http.StatusForbidden {
+ t.Fatal("Should have been forbidden")
+ }
- Client.LoginByEmail(team.Domain, user2.Email, "pwd")
- post5 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
- _, err = Client.CreateValetPost(post5)
- if err != nil {
- t.Fatal(err)
- }
+ Client.LoginByEmail(team.Domain, user2.Email, "pwd")
+ post5 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
+ _, err = Client.CreateValetPost(post5)
+ if err != nil {
+ t.Fatal(err)
+ }
- user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
- user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
- Srv.Store.User().VerifyEmail(user3.Id)
+ user3 := &model.User{TeamId: team2.Id, Email: model.NewId() + "corey@test.com", FullName: "Corey Hulen", Password: "pwd"}
+ user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
+ Srv.Store.User().VerifyEmail(user3.Id)
- Client.LoginByEmail(team2.Domain, user3.Email, "pwd")
+ Client.LoginByEmail(team2.Domain, 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)
+ 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)
- post6 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
- _, err = Client.CreateValetPost(post6)
- if err.StatusCode != http.StatusForbidden {
- t.Fatal("Should have been forbidden")
- }
+ post6 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a"}
+ _, err = Client.CreateValetPost(post6)
+ if err.StatusCode != http.StatusForbidden {
+ t.Fatal("Should have been forbidden")
+ }
- if _, err = Client.DoPost("/channels/"+channel3.Id+"/create", "garbage"); err == nil {
- t.Fatal("should have been an error")
+ if _, err = Client.DoPost("/channels/"+channel3.Id+"/create", "garbage"); err == nil {
+ t.Fatal("should have been an error")
+ }
+ } else {
+ post1 := &model.Post{ChannelId: channel1.Id, Message: "#hashtag a" + model.NewId() + "a"}
+ _, err := Client.CreateValetPost(post1)
+ if err.StatusCode != http.StatusNotImplemented {
+ t.Fatal("Should have failed with 501 - Not Implemented")
+ }
}
}
diff --git a/api/team.go b/api/team.go
index b04d8c588..cb60602c6 100644
--- a/api/team.go
+++ b/api/team.go
@@ -157,9 +157,11 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- CreateValet(c, rteam)
- if c.Err != nil {
- return
+ if utils.Cfg.TeamSettings.AllowValet {
+ CreateValet(c, rteam)
+ if c.Err != nil {
+ return
+ }
}
InviteMembers(rteam, ruser, teamSignup.Invites)
diff --git a/api/user.go b/api/user.go
index c0ebc05e0..83e29b28e 100644
--- a/api/user.go
+++ b/api/user.go
@@ -145,6 +145,10 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) {
}
func CreateValet(c *Context, team *model.Team) *model.User {
+ if !utils.Cfg.TeamSettings.AllowValet {
+ return &model.User{}
+ }
+
valet := &model.User{}
valet.TeamId = team.Id
valet.Email = utils.Cfg.EmailSettings.FeedbackEmail
diff --git a/config/config.json b/config/config.json
index 955947a83..c75f2f15a 100644
--- a/config/config.json
+++ b/config/config.json
@@ -73,6 +73,7 @@
"TeamSettings": {
"MaxUsersPerTeam": 150,
"AllowPublicLink": true,
+ "AllowValet": false,
"TermsLink": "/static/help/configure_links.html",
"PrivacyLink": "/static/help/configure_links.html",
"AboutLink": "/static/help/configure_links.html",
diff --git a/utils/config.go b/utils/config.go
index 37da59315..76e6060be 100644
--- a/utils/config.go
+++ b/utils/config.go
@@ -97,6 +97,7 @@ type PrivacySettings struct {
type TeamSettings struct {
MaxUsersPerTeam int
AllowPublicLink bool
+ AllowValet bool
TermsLink string
PrivacyLink string
AboutLink string
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
index 3534c7573..9bcbad079 100644
--- a/web/react/components/create_comment.jsx
+++ b/web/react/components/create_comment.jsx
@@ -55,12 +55,15 @@ module.exports = React.createClass({
}.bind(this),
function(err) {
- var state = {}
+ var state = {};
state.server_error = err.message;
- this.setState(state);
+
if (err.message === "Invalid RootId parameter") {
if ($('#post_deleted').length > 0) $('#post_deleted').modal('show');
}
+ else {
+ this.setState(state);
+ }
}.bind(this)
);
},
diff --git a/web/react/components/find_team.jsx b/web/react/components/find_team.jsx
index 329592a73..91a842ffc 100644
--- a/web/react/components/find_team.jsx
+++ b/web/react/components/find_team.jsx
@@ -56,7 +56,7 @@ module.exports = React.createClass({
<div>
<h4>Find Your Team</h4>
<form onSubmit={this.handleSubmit}>
- <p>{"An email will be sent to this address with links to any " + strings.TeamPlural}</p>
+ <p>{"We'll send you an email with links to your " + strings.TeamPlural + "."}</p>
<div className="form-group">
<label className='control-label'>Email</label>
<div className={ email_error ? "form-group has-error" : "form-group" }>
diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx
index 8d82a4b62..685e1b058 100644
--- a/web/react/components/login.jsx
+++ b/web/react/components/login.jsx
@@ -74,7 +74,7 @@ var FindTeamDomain = React.createClass({
<br/>
<br/>
<div>
- <span>{"Want to create your own " + strings.Team + "?"} <a href="/" className="signup-team-login">Sign up now</a></span>
+ <span>{"Want to create your own " + strings.Team + "?"} <a href={config.HomeLink} className="signup-team-login">Sign up now</a></span>
</div>
</form>
</div>
diff --git a/web/react/components/post_deleted_modal.jsx b/web/react/components/post_deleted_modal.jsx
index 307120df3..83b007bad 100644
--- a/web/react/components/post_deleted_modal.jsx
+++ b/web/react/components/post_deleted_modal.jsx
@@ -23,7 +23,7 @@ module.exports = React.createClass({
<p>Someone deleted the message on which you tried to post a comment.</p>
</div>
<div className="modal-footer">
- <button type="button" className="btn btn-primary" data-dismiss="modal">Agree</button>
+ <button type="button" className="btn btn-primary" data-dismiss="modal">Okay</button>
</div>
</div>
</div>
diff --git a/web/react/components/settings_sidebar.jsx b/web/react/components/settings_sidebar.jsx
index 34e3c9203..a1546890f 100644
--- a/web/react/components/settings_sidebar.jsx
+++ b/web/react/components/settings_sidebar.jsx
@@ -14,11 +14,13 @@ module.exports = React.createClass({
<li className={this.props.activeTab == 'general' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("general");}}><i className="glyphicon glyphicon-cog"></i>General</a></li>
<li className={this.props.activeTab == 'security' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("security");}}><i className="glyphicon glyphicon-lock"></i>Security</a></li>
<li className={this.props.activeTab == 'notifications' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("notifications");}}><i className="glyphicon glyphicon-exclamation-sign"></i>Notifications</a></li>
- <li className={this.props.activeTab == 'sessions' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("sessions");}}><i className="glyphicon glyphicon-globe"></i>Sessions</a></li>
- <li className={this.props.activeTab == 'activity_log' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("activity_log");}}><i className="glyphicon glyphicon-time"></i>Activity Log</a></li>
<li className={this.props.activeTab == 'appearance' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("appearance");}}><i className="glyphicon glyphicon-wrench"></i>Appearance</a></li>
</ul>
</div>
);
+ /* Temporarily removing sessions and activity logs
+ <li className={this.props.activeTab == 'sessions' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("sessions");}}><i className="glyphicon glyphicon-globe"></i>Sessions</a></li>
+ <li className={this.props.activeTab == 'activity_log' ? 'active' : ''}><a href="#" onClick={function(){self.updateTab("activity_log");}}><i className="glyphicon glyphicon-time"></i>Activity Log</a></li>
+ */
}
});
diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx
index b165a59ad..a9c2433f2 100644
--- a/web/react/components/user_settings.jsx
+++ b/web/react/components/user_settings.jsx
@@ -1126,6 +1126,9 @@ module.exports = React.createClass({
<NotificationsTab user={this.state.user} activeSection={this.props.activeSection} updateSection={this.props.updateSection} />
</div>
);
+
+ /* Temporarily removing sessions and activity_log tabs
+
} else if (this.props.activeTab === 'sessions') {
return (
<div>
@@ -1138,6 +1141,8 @@ module.exports = React.createClass({
<AuditTab activeSection={this.props.activeSection} updateSection={this.props.updateSection} />
</div>
);
+ */
+
} else if (this.props.activeTab === 'appearance') {
return (
<div>
diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx
index 7d0f0d8a9..4cb30e1d3 100644
--- a/web/react/components/view_image.jsx
+++ b/web/react/components/view_image.jsx
@@ -165,7 +165,7 @@ module.exports = React.createClass({
<span className="text"> | </span>
</div>
: "" }
- <a href={this.props.filenames[id]} download={name} className="text">Download</a>
+ <a href={this.props.filenames[id]} download={decodeURIComponent(name)} className="text">Download</a>
</div>
</div>
{loading}
diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx
index b83ee22e7..786e6dcea 100644
--- a/web/react/utils/client.jsx
+++ b/web/react/utils/client.jsx
@@ -4,12 +4,12 @@
module.exports.track = function(category, action, label, prop, val) {
global.window.snowplow('trackStructEvent', category, action, label, prop, val);
- if (global.window.analytics != null) global.window.analytics.track(action, {category: category, label: label, property: prop, value: val});
+ global.window.analytics.track(action, {category: category, label: label, property: prop, value: val});
};
module.exports.trackPage = function() {
global.window.snowplow('trackPageView');
- if (global.window.analytics != null) global.window.analytics.page();
+ global.window.analytics.page();
};
function handleError(method_name, xhr, status, err) {
diff --git a/web/templates/head.html b/web/templates/head.html
index 5fd3ee104..5eb7a7333 100644
--- a/web/templates/head.html
+++ b/web/templates/head.html
@@ -36,6 +36,7 @@
window._LTracker = _LTracker;
_LTracker.push({'logglyKey': config.LogglyWriteKey, 'sendConsoleErrors' : config.LogglyConsoleErrors });
} else {
+ window._LTracker = [];
console.warn("config.js missing LogglyWriteKey, Loggly analytics is not reporting");
}
</script>
@@ -58,26 +59,34 @@
analytics.page();
}}();
} else {
+ analytics = {};
+ analytics.page = function(){};
+ analytics.track = function(){};
console.warn("config.js missing SegmentWriteKey, SegmentIO analytics is not tracking");
}
</script>
<!-- Snowplow starts plowing -->
<script type="text/javascript">
- ;(function(p,l,o,w,i,n,g){if(!p[i]){p.GlobalSnowplowNamespace=p.GlobalSnowplowNamespace||[];
- p.GlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments)
- };p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1;
- n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","//d1fc8wv8zag5ca.cloudfront.net/2.4.2/sp.js","snowplow"));
+ if ('{{ .Props.AnalyticsUrl }}'.trim() !== '') {
+ ;(function(p,l,o,w,i,n,g){if(!p[i]){p.GlobalSnowplowNamespace=p.GlobalSnowplowNamespace||[];
+ p.GlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments)
+ };p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1;
+ n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","//d1fc8wv8zag5ca.cloudfront.net/2.4.2/sp.js","snowplow"));
- window.snowplow('newTracker', 'cf', '{{ .Props.AnalyticsUrl }}', {
- appId: '{{ .SiteName }}'
- });
+ window.snowplow('newTracker', 'cf', '{{ .Props.AnalyticsUrl }}', {
+ appId: '{{ .SiteName }}'
+ });
- var user = window.UserStore.getCurrentUser(true);
- if (user) {
- window.snowplow('setUserId', user.id);
- }
+ var user = window.UserStore.getCurrentUser(true);
+ if (user) {
+ window.snowplow('setUserId', user.id);
+ }
- window.snowplow('trackPageView');
+ window.snowplow('trackPageView');
+ } else {
+ window.snowplow = function(){};
+ console.warn("config.json missing AnalyticsUrl, Snowplow analytics is not tracking");
+ }
</script>
<!-- Snowplow stops plowing -->
</head>