diff --git a/CHANGELOG.md b/CHANGELOG.md index 49a0ead8943..54d9122c004 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -145,7 +145,6 @@ Messaging, Comments and Notifications - Full markdown support in messages, comments, and channel description - Support for emoji codes rendering to image files - Files and Images - Added ability to play video and audio files @@ -198,86 +197,86 @@ Licensing ### Bug Fixes -- Fixed issue so that SSO option automatically set EmailVerified=true (it was false previously) +- Fixed issue so that SSO option automatically set `EmailVerified=true` (it was false previously) ### Compatibility A large number of settings were changed in [`config.json`](./config/config.json) and a System Console UI was added. This is a very large change due to Mattermost releasing as v1.0 and it's unlikely a change of this size would happen again. -Prior to upgrading the Mattermost binaries from the previous versions, the below options would need to be manually updated in existing config.json file to migrate successfully. This is a list of changes and their new default values in a fresh install: +Prior to upgrading the Mattermost binaries from the previous versions, the below options would need to be manually updated in your existing config.json file to migrate successfully. This is a list of changes and their new default values in a fresh install: #### Config.json Changes from v0.7 to v1.0 ##### Service Settings - Under `ServiceSettings` in [`config.json`](./config/config.json): - - **Moved:** `"SiteName": "Mattermost"` which was added to `TeamSettings` - - **Removed:** `"Mode" : "dev"` which deprecates a high level dev mode, now replaced by granular controls - - **Renamed:** `"AllowTesting" : false` to `"EnableTesting": false` which allows the use of `/loadtest` slash commands during development - - **Removed:** `"UseSSL": false` boolean replaced by `"ConnectionSecurity": ""` under `Security` with new options: _None_ (`""`), _TLS_ (`"TLS"`) and _StartTLS_ ('"StartTLS"`) - - **Renamed**: `"Port": "8065"` to `"ListenAddress": ":8065"` to define address on which to listen. Must be prepended with a colon. - - **Removed:** `"Version": "developer"` removed and version information now stored in `model/version.go` - - **Removed:** `"Shards": {}` which was not used - - **Moved:** `"InviteSalt": "gxHVDcKUyP2y1eiyW8S8na1UYQAfq6J6"` to `EmailSettings` - - **Moved:** `"PublicLinkSalt": "TO3pTyXIZzwHiwyZgGql7lM7DG3zeId4"` to `FileSettings` - - **Renamed and Moved** `"ResetSalt": "IPxFzSfnDFsNsRafZxz8NaYqFKhf9y2t"` to `"PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5eL"` and moved to `EmailSettings` - - **Removed:** `"AnalyticsUrl": ""` which was not used - - **Removed:** `"UseLocalStorage": true` which is replaced by `"DriverName": "local"` in `FileSettings` - - **Renamed and Moved:** `"StorageDirectory": "./data/"` to `Directory` and moved to `FileSettings` - - **Renamed:** `"AllowedLoginAttempts": 10` to `"MaximumLoginAttempts": 10` - - **Renamed, Reversed and Moved:** `"DisableEmailSignUp": false` renamed `"EnableSignUpWithEmail": true`, reversed meaning of `true`, and moved to `EmailSettings` - - **Added:** `"EnableOAuthServiceProvider": false` to enable OAuth2 service provider functionality - - **Added:** `"EnableIncomingWebhooks": false` to enable incoming webhooks feature + - Moved: `"SiteName": "Mattermost"` which was added to `TeamSettings` + - Removed: `"Mode" : "dev"` which deprecates a high level dev mode, now replaced by granular controls + - Renamed: `"AllowTesting" : false` to `"EnableTesting": false` which allows the use of `/loadtest` slash commands during development + - Removed: `"UseSSL": false` boolean replaced by `"ConnectionSecurity": ""` under `Security` with new options: _None_ (`""`), _TLS_ (`"TLS"`) and _StartTLS_ ('"StartTLS"`) + - Renamed: `"Port": "8065"` to `"ListenAddress": ":8065"` to define address on which to listen. Must be prepended with a colon. + - Removed: `"Version": "developer"` removed and version information now stored in `model/version.go` + - Removed: `"Shards": {}` which was not used + - Moved: `"InviteSalt": "gxHVDcKUyP2y1eiyW8S8na1UYQAfq6J6"` to `EmailSettings` + - Moved: `"PublicLinkSalt": "TO3pTyXIZzwHiwyZgGql7lM7DG3zeId4"` to `FileSettings` + - Renamed and Moved `"ResetSalt": "IPxFzSfnDFsNsRafZxz8NaYqFKhf9y2t"` to `"PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5eL"` and moved to `EmailSettings` + - Removed: `"AnalyticsUrl": ""` which was not used + - Removed: `"UseLocalStorage": true` which is replaced by `"DriverName": "local"` in `FileSettings` + - Renamed and Moved: `"StorageDirectory": "./data/"` to `Directory` and moved to `FileSettings` + - Renamed: `"AllowedLoginAttempts": 10` to `"MaximumLoginAttempts": 10` + - Renamed, Reversed and Moved: `"DisableEmailSignUp": false` renamed `"EnableSignUpWithEmail": true`, reversed meaning of `true`, and moved to `EmailSettings` + - Added: `"EnableOAuthServiceProvider": false` to enable OAuth2 service provider functionality + - Added: `"EnableIncomingWebhooks": false` to enable incoming webhooks feature ##### Team Settings - Under `TeamSettings` in [`config.json`](./config/config.json): - - **Renamed:** `"AllowPublicLink": true` renamed to `"EnablePublicLink": true` and moved to `FileSettings` - - **Removed:** `AllowValetDefault` which was a guest account feature that is deprecated - - **Removed:** `"TermsLink": "/static/help/configure_links.html"` removed since option didn't need configuration - - **Removed:** `"PrivacyLink": "/static/help/configure_links.html"` removed since option didn't need configuration - - **Removed:** `"AboutLink": "/static/help/configure_links.html"` removed since option didn't need configuration - - **Removed:** `"HelpLink": "/static/help/configure_links.html"` removed since option didn't need configuration - - **Removed:** `"ReportProblemLink": "/static/help/configure_links.html"` removed since option didn't need configuration - - **Removed:** `"TourLink": "/static/help/configure_links.html"` removed since option didn't need configuration - - **Removed:** `"DefaultThemeColor": "#2389D7"` removed since theme colors changed from 1 to 18, default theme color option may be added back later after theme color design stablizes - - **Renamed:** `"DisableTeamCreation": false` to `"EnableUserCreation": true` and reversed - - **Added:** ` "EnableUserCreation": true` added to disable ability to create new user accounts in the system + - Renamed: `"AllowPublicLink": true` renamed to `"EnablePublicLink": true` and moved to `FileSettings` + - Removed: `AllowValetDefault` which was a guest account feature that is deprecated + - Removed: `"TermsLink": "/static/help/configure_links.html"` removed since option didn't need configuration + - Removed: `"PrivacyLink": "/static/help/configure_links.html"` removed since option didn't need configuration + - Removed: `"AboutLink": "/static/help/configure_links.html"` removed since option didn't need configuration + - Removed: `"HelpLink": "/static/help/configure_links.html"` removed since option didn't need configuration + - Removed: `"ReportProblemLink": "/static/help/configure_links.html"` removed since option didn't need configuration + - Removed: `"TourLink": "/static/help/configure_links.html"` removed since option didn't need configuration + - Removed: `"DefaultThemeColor": "#2389D7"` removed since theme colors changed from 1 to 18, default theme color option may be added back later after theme color design stablizes + - Renamed: `"DisableTeamCreation": false` to `"EnableUserCreation": true` and reversed + - Added: ` "EnableUserCreation": true` added to disable ability to create new user accounts in the system ##### SSO Settings - Under `SSOSettings` in [`config.json`](./config/config.json): - - **Renamed Category:** `SSOSettings` to `GitLabSettings` - - **Renamed:** `"Allow": false` to `"Enable": false` to enable GitLab SSO + - Renamed Category: `SSOSettings` to `GitLabSettings` + - Renamed: `"Allow": false` to `"Enable": false` to enable GitLab SSO ##### AWS Settings - Under `AWSSettings` in [`config.json`](./config/config.json): - This section was removed and settings moved to `FileSettings` - - **Renamed and Moved:** `"S3AccessKeyId": ""` renamed `"AmazonS3AccessKeyId": "",` and moved to `FileSettings` - - **Renamed and Moved:** `"S3SecretAccessKey": ""` renamed `"AmazonS3SecretAccessKey": "",` and moved to `FileSettings` - - **Renamed and Moved:** `"S3Bucket": ""` renamed `"AmazonS3Bucket": "",` and moved to `FileSettings` - - **Renamed and Moved:** `"S3Region": ""` renamed `"AmazonS3Region": "",` and moved to `FileSettings` + - Renamed and Moved: `"S3AccessKeyId": ""` renamed `"AmazonS3AccessKeyId": "",` and moved to `FileSettings` + - Renamed and Moved: `"S3SecretAccessKey": ""` renamed `"AmazonS3SecretAccessKey": "",` and moved to `FileSettings` + - Renamed and Moved: `"S3Bucket": ""` renamed `"AmazonS3Bucket": "",` and moved to `FileSettings` + - Renamed and Moved: `"S3Region": ""` renamed `"AmazonS3Region": "",` and moved to `FileSettings` ##### Image Settings - Under `ImageSettings` in [`config.json`](./config/config.json): - - **Renamed:** `"ImageSettings"` section to `"FileSettings"` - - **Added:** `"DriverName" : "local"` to specify the file storage method, `amazons3` can also be used to setup S3 + - Renamed: `"ImageSettings"` section to `"FileSettings"` + - Added: `"DriverName" : "local"` to specify the file storage method, `amazons3` can also be used to setup S3 ##### EmailSettings - Under `EmailSettings` in [`config.json`](./config/config.json): - - **Removed:** `"ByPassEmail": "true"` which is replaced with `SendEmailNotifications` and `RequireEmailVerification` - - **Added:** `"SendEmailNotifications" : "false"` to control whether email notifications are sent - - **Added:** `"RequireEmailVerification" : "false"` to control if users need to verify their emails - - **Replaced:** `"UseTLS": "false"` with `"ConnectionSecurity": ""` with options: _None_ (`""`), _TLS_ (`"TLS"`) and _StartTLS_ ('"StartTLS"`) - - **Replaced:** `"UseStartTLS": "false"` with `"ConnectionSecurity": ""` with options: _None_ (`""`), _TLS_ (`"TLS"`) and _StartTLS_ ('"StartTLS"`) + - Removed: `"ByPassEmail": "true"` which is replaced with `SendEmailNotifications` and `RequireEmailVerification` + - Added: `"SendEmailNotifications" : "false"` to control whether email notifications are sent + - Added: `"RequireEmailVerification" : "false"` to control if users need to verify their emails + - Replaced: `"UseTLS": "false"` with `"ConnectionSecurity": ""` with options: _None_ (`""`), _TLS_ (`"TLS"`) and _StartTLS_ (`"StartTLS"`) + - Replaced: `"UseStartTLS": "false"` with `"ConnectionSecurity": ""` with options: _None_ (`""`), _TLS_ (`"TLS"`) and _StartTLS_ (`"StartTLS"`) ##### Privacy Settings - Under `PrivacySettings` in [`config.json`](./config/config.json): - - **Removed:** `"ShowPhoneNumber": "true"` which was not used - - **Removed:** `"ShowSkypeId" : "true"` which was not used + - Removed: `"ShowPhoneNumber": "true"` which was not used + - Removed: `"ShowSkypeId" : "true"` which was not used ### Database Changes from v0.7 to v1.0 diff --git a/api/command.go b/api/command.go index 52ff8fffdd0..54f863c484a 100644 --- a/api/command.go +++ b/api/command.go @@ -22,6 +22,7 @@ var commands = []commandHandler{ joinCommand, loadTestCommand, echoCommand, + shrugCommand, } var echoSem chan bool @@ -160,6 +161,34 @@ func echoCommand(c *Context, command *model.Command) bool { return false } +func shrugCommand(c *Context, command *model.Command) bool { + cmd := "/shrug" + + if !command.Suggest && strings.Index(command.Command, cmd) == 0 { + message := "¯\\_(ツ)_/¯" + + parameters := strings.SplitN(command.Command, " ", 2) + if len(parameters) > 1 { + message += " " + parameters[1] + } + + post := &model.Post{} + post.Message = message + post.ChannelId = command.ChannelId + if _, err := CreatePost(c, post, false); err != nil { + l4g.Error("Unable to create /shrug post post, err=%v", err) + return false + } + command.Response = model.RESP_EXECUTED + return true + + } else if strings.Index(cmd, command.Command) == 0 { + command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: "Adds ¯\\_(ツ)_/¯ to your message, /shrug [message]"}) + } + + return false +} + func joinCommand(c *Context, command *model.Command) bool { // looks for "/join channel-name" diff --git a/api/file.go b/api/file.go index 142ef7ac77e..94eea516ad8 100644 --- a/api/file.go +++ b/api/file.go @@ -23,6 +23,7 @@ import ( "image/jpeg" "io" "io/ioutil" + "mime" "net/http" "net/url" "os" @@ -331,9 +332,18 @@ func getFileInfo(c *Context, w http.ResponseWriter, r *http.Request) { w.Header().Set("Cache-Control", "max-age=2592000, public") + var mimeType string + ext := filepath.Ext(filename) + if model.IsFileExtImage(ext) { + mimeType = model.GetImageMimeType(ext) + } else { + mimeType = mime.TypeByExtension(ext) + } + result := make(map[string]string) result["filename"] = filename result["size"] = size + result["mime"] = mimeType w.Write([]byte(model.MapToJson(result))) } diff --git a/api/team.go b/api/team.go index 666fd909b4c..d39d8ed60cf 100644 --- a/api/team.go +++ b/api/team.go @@ -108,7 +108,7 @@ func createTeamFromSSO(c *Context, w http.ResponseWriter, r *http.Request) { team.Name = model.CleanTeamName(team.Name) - if err := team.IsValid(); err != nil { + if err := team.IsValid(*utils.Cfg.TeamSettings.RestrictTeamNames); err != nil { c.Err = err return } @@ -164,7 +164,7 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) { teamSignup.Team.PreSave() - if err := teamSignup.Team.IsValid(); err != nil { + if err := teamSignup.Team.IsValid(*utils.Cfg.TeamSettings.RestrictTeamNames); err != nil { c.Err = err return } @@ -379,11 +379,6 @@ func FindTeamByName(c *Context, name string, all string) bool { return false } - if model.IsReservedTeamName(name) { - c.Err = model.NewAppError("findTeamByName", "This URL is unavailable. Please try another.", "name="+name) - return false - } - if result := <-Srv.Store.Team().GetByName(name); result.Err != nil { return false } else { diff --git a/config/config.json b/config/config.json index 37109428dac..7bac58df7b1 100644 --- a/config/config.json +++ b/config/config.json @@ -17,7 +17,8 @@ "MaxUsersPerTeam": 50, "EnableTeamCreation": true, "EnableUserCreation": true, - "RestrictCreationToDomains": "" + "RestrictCreationToDomains": "", + "RestrictTeamNames": true }, "SqlSettings": { "DriverName": "mysql", diff --git a/doc/help/Search.md b/doc/help/Search.md new file mode 100644 index 00000000000..f36e079bdc6 --- /dev/null +++ b/doc/help/Search.md @@ -0,0 +1,11 @@ +# Search + +The search box in Mattermost brings back results from any channel of which you’re a member. No results are returned from channels where you are not a member - even if they are open channels. + +Some things to know about search: + +- Multiple search terms are connected with “OR” by default. Typing in `Mattermost website` returns results containing “Mattermost” or “website” +- You can use quotes to return search results for exact terms, like `"Mattermost website"` will only return messages containing the entire phrase `"Mattermost website"` and not return messages with only `Mattermost` or `website` +- You can use the `*` character for wildcard searches that match within words. For example: Searching for `rea*` brings back messages containing `reach`, `reason` and other words starting with `rea`. + +Search in Mattermost uses the full text search features in MySQL and Postgres databases. Special cases that are not supported in default full text search, such as searching for IP addresses like `10.100.200.101`, can be added in future as the search feature evolves. diff --git a/doc/install/Troubleshooting.md b/doc/install/Troubleshooting.md index 6a7260ddfa2..21839c86f4b 100644 --- a/doc/install/Troubleshooting.md +++ b/doc/install/Troubleshooting.md @@ -1,12 +1,16 @@ -### Mattermost Troubleshooting +# Mattermost Troubleshooting #### Important notes -1. **DO NOT manipulate the Mattermost database** +##### **DO NOT manipulate the Mattermost database** - 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. #### Common Issues -1. Error message in logs when attempting to sign-up: `x509: certificate signed by unknown authority` +##### Error message in logs when attempting to sign-up: `x509: certificate signed by unknown authority` - This error may appear when attempt to use a self-signed certificate to setup SSL, which is not yet supported by Mattermost. You can resolve this issue by setting up a load balancer like Ngnix. A ticket exists to [add support for self-signed certificates in future](x509: certificate signed by unknown authority). + +##### Lost System Administrator account + - If the System Administrator account becomes unavailable, a person leaving the organization for example, you can set a new system admin from the commandline using `./platform -assign_role -team_name="yourteam" -email="you@example.com" -role="system_admin"` + diff --git a/doc/install/Upgrade-Guide.md b/doc/install/Upgrade-Guide.md index e86cf816629..cecd45353fc 100644 --- a/doc/install/Upgrade-Guide.md +++ b/doc/install/Upgrade-Guide.md @@ -1,12 +1,14 @@ # Mattermost Upgrade Guide -### Upgrading Mattermost v0.7 to v1.1 +### Upgrading Mattermost v0.7 to v1.1.1 -If you've manually changed Mattermost v0.7 configuration by updating the `config.json` file, you'll need to port those changes to Mattermost v1.1: +_Note: [Mattermost v1.1.1](https://github.com/mattermost/platform/releases/tag/v1.1.1) is a special release of Mattermost v1.1 that upgrades the database to Mattermost v1.1 from EITHER Mattermost v0.7 or Mattermost v1.0. The following instructions are for upgrading from Mattermost v0.7 to v1.1.1 and skipping the upgrade to Mattermost v1.0._ + +If you've manually changed Mattermost v0.7 configuration by updating the `config.json` file, you'll need to port those changes to Mattermost v1.1.1: 1. Go to the `config.json` file that you manually updated and note any differences from the [default `config.json` file in Mattermost 0.7](https://github.com/mattermost/platform/blob/v0.7.0/config/config.json). -2. For each setting that you changed, check [the changelog documentation](https://github.com/mattermost/platform/blob/master/CHANGELOG.md#configjson-changes-from-v07-to-v10) on whether the configuration setting has changed between v0.7 and v1.1 +2. For each setting that you changed, check [the changelog documentation](https://github.com/mattermost/platform/blob/master/CHANGELOG.md#configjson-changes-from-v07-to-v10) on whether the configuration setting has changed between v0.7 and v1.1.1 3. Update your new [`config.json` file in Mattermost v1.1](https://github.com/mattermost/platform/blob/v1.1.0/config/config.json), based on your preferences and the changelog documentation above. diff --git a/model/config.go b/model/config.go index 3a39df2f19b..216b1de8601 100644 --- a/model/config.go +++ b/model/config.go @@ -122,6 +122,7 @@ type TeamSettings struct { EnableTeamCreation bool EnableUserCreation bool RestrictCreationToDomains string + RestrictTeamNames *bool } type Config struct { @@ -169,6 +170,11 @@ func (o *Config) SetDefaults() { o.ServiceSettings.EnableSecurityFixAlert = new(bool) *o.ServiceSettings.EnableSecurityFixAlert = true } + + if o.TeamSettings.RestrictTeamNames == nil { + o.TeamSettings.RestrictTeamNames = new(bool) + *o.TeamSettings.RestrictTeamNames = true + } } func (o *Config) IsValid() *AppError { diff --git a/model/team.go b/model/team.go index 584c78f8d8f..9da2cd5b2ab 100644 --- a/model/team.go +++ b/model/team.go @@ -97,7 +97,7 @@ func (o *Team) Etag() string { return Etag(o.Id, o.UpdateAt) } -func (o *Team) IsValid() *AppError { +func (o *Team) IsValid(restrictTeamNames bool) *AppError { if len(o.Id) != 26 { return NewAppError("Team.IsValid", "Invalid Id", "") @@ -127,7 +127,7 @@ func (o *Team) IsValid() *AppError { return NewAppError("Team.IsValid", "Invalid URL Identifier", "id="+o.Id) } - if IsReservedTeamName(o.Name) { + if restrictTeamNames && IsReservedTeamName(o.Name) { return NewAppError("Team.IsValid", "This URL is unavailable. Please try another.", "id="+o.Id) } diff --git a/model/team_test.go b/model/team_test.go index fd2428f03a1..112d48a9daf 100644 --- a/model/team_test.go +++ b/model/team_test.go @@ -21,45 +21,45 @@ func TestTeamJson(t *testing.T) { func TestTeamIsValid(t *testing.T) { o := Team{} - if err := o.IsValid(); err == nil { + if err := o.IsValid(true); err == nil { t.Fatal("should be invalid") } o.Id = NewId() - if err := o.IsValid(); err == nil { + if err := o.IsValid(true); err == nil { t.Fatal("should be invalid") } o.CreateAt = GetMillis() - if err := o.IsValid(); err == nil { + if err := o.IsValid(true); err == nil { t.Fatal("should be invalid") } o.UpdateAt = GetMillis() - if err := o.IsValid(); err == nil { + if err := o.IsValid(true); err == nil { t.Fatal("should be invalid") } o.Email = strings.Repeat("01234567890", 20) - if err := o.IsValid(); err == nil { + if err := o.IsValid(true); err == nil { t.Fatal("should be invalid") } o.Email = "corey@hulen.com" o.DisplayName = strings.Repeat("01234567890", 20) - if err := o.IsValid(); err == nil { + if err := o.IsValid(true); err == nil { t.Fatal("should be invalid") } o.DisplayName = "1234" o.Name = "ZZZZZZZ" - if err := o.IsValid(); err == nil { + if err := o.IsValid(true); err == nil { t.Fatal("should be invalid") } o.Name = "zzzzz" o.Type = TEAM_OPEN - if err := o.IsValid(); err != nil { + if err := o.IsValid(true); err != nil { t.Fatal(err) } } diff --git a/store/sql_team_store.go b/store/sql_team_store.go index 2d65435b0be..380d979bd39 100644 --- a/store/sql_team_store.go +++ b/store/sql_team_store.go @@ -5,6 +5,7 @@ package store import ( "github.com/mattermost/platform/model" + "github.com/mattermost/platform/utils" ) type SqlTeamStore struct { @@ -52,7 +53,7 @@ func (s SqlTeamStore) Save(team *model.Team) StoreChannel { team.PreSave() - if result.Err = team.IsValid(); result.Err != nil { + if result.Err = team.IsValid(*utils.Cfg.TeamSettings.RestrictTeamNames); result.Err != nil { storeChannel <- result close(storeChannel) return @@ -84,7 +85,7 @@ func (s SqlTeamStore) Update(team *model.Team) StoreChannel { team.PreUpdate() - if result.Err = team.IsValid(); result.Err != nil { + if result.Err = team.IsValid(*utils.Cfg.TeamSettings.RestrictTeamNames); result.Err != nil { storeChannel <- result close(storeChannel) return diff --git a/utils/config.go b/utils/config.go index c1e3a8d2aaf..fd9856a67c6 100644 --- a/utils/config.go +++ b/utils/config.go @@ -182,6 +182,7 @@ func getClientConfig(c *model.Config) map[string]string { props["SiteName"] = c.TeamSettings.SiteName props["EnableTeamCreation"] = strconv.FormatBool(c.TeamSettings.EnableTeamCreation) + props["RestrictTeamNames"] = strconv.FormatBool(*c.TeamSettings.RestrictTeamNames) props["EnableOAuthServiceProvider"] = strconv.FormatBool(c.ServiceSettings.EnableOAuthServiceProvider) diff --git a/web/react/components/admin_console/team_settings.jsx b/web/react/components/admin_console/team_settings.jsx index da4299714a2..9ecd14a1e92 100644 --- a/web/react/components/admin_console/team_settings.jsx +++ b/web/react/components/admin_console/team_settings.jsx @@ -31,6 +31,7 @@ export default class TeamSettings extends React.Component { config.TeamSettings.RestrictCreationToDomains = ReactDOM.findDOMNode(this.refs.RestrictCreationToDomains).value.trim(); config.TeamSettings.EnableTeamCreation = ReactDOM.findDOMNode(this.refs.EnableTeamCreation).checked; config.TeamSettings.EnableUserCreation = ReactDOM.findDOMNode(this.refs.EnableUserCreation).checked; + config.TeamSettings.RestrictTeamNames = ReactDOM.findDOMNode(this.refs.RestrictTeamNames).checked; var MaxUsersPerTeam = 50; if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MaxUsersPerTeam).value, 10))) { @@ -208,6 +209,39 @@ export default class TeamSettings extends React.Component { +
{'When true, You cannot create a team name with reserved words like www, admin, support, test, channel, etc'}
+
+ );
+ playbackControls = '';
+ }
+
+ var thumbnail;
+ if (type === 'image') {
+ if (this.state.playing) {
+ thumbnail = (
+
+ {'Drop a file to upload it.'}
+
+ );
+ playbackControls = '';
+ }
+
// image files just show a preview of the file
content = (
+ {loadingIndicator}
+ {playbackControls}
${text}
`; + let outText = text; + + if (!('emoticons' in this.options) || this.options.emoticon) { + outText = TextFormatting.doFormatEmoticons(text); } - return super.paragraph(text); + if (this.formattingOptions.singleline) { + return `${outText}
`; + } + + return super.paragraph(outText); } table(header, body) { diff --git a/web/react/utils/text_formatting.jsx b/web/react/utils/text_formatting.jsx index d79aeed68c7..5c2e68f1e65 100644 --- a/web/react/utils/text_formatting.jsx +++ b/web/react/utils/text_formatting.jsx @@ -69,6 +69,15 @@ export function doFormatText(text, options) { return output; } +export function doFormatEmoticons(text) { + const tokens = new Map(); + + let output = Emoticons.handleEmoticons(text, tokens); + output = replaceTokens(output, tokens); + + return output; +} + export function sanitizeHtml(text) { let output = text; diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 53e32838426..c72c3f32c25 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -544,6 +544,7 @@ export function applyTheme(theme) { if (theme.buttonBg) { changeCss('.btn.btn-primary', 'background:' + theme.buttonBg, 1); changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background:' + changeColor(theme.buttonBg, -0.25), 1); + changeCss('.file-playback-controls', 'color:' + changeColor(theme.buttonBg, -0.25), 1); } if (theme.buttonColor) { diff --git a/web/sass-files/sass/partials/_files.scss b/web/sass-files/sass/partials/_files.scss index 01057423db7..d3ab3b9f849 100644 --- a/web/sass-files/sass/partials/_files.scss +++ b/web/sass-files/sass/partials/_files.scss @@ -133,12 +133,34 @@ height: 100%; background-color: #FFF; background-repeat: no-repeat; + overflow: hidden; + position: relative; + text-align: center; &.small { background-position: center; } &.normal { background-position: top left; } + .spinner.file__loading { + position: absolute; + left: 50%; + margin-left: -16px; + top: 50%; + margin-top: -16px; + } + .file__loaded { + max-width: initial; + &.landscape, &.quadrat { + height: 100px; + } + &.portrait { + width: 120px; + } + } + &:hover .file-playback-controls.stop { + @include opacity(1); + } } .post-image__thumbnail { float: left; @@ -215,3 +237,20 @@ } } } + +.file-playback-controls { + position: absolute; + right: 5px; + bottom: 0; + font-size: 22px; + cursor: pointer; + z-index: 2; + -webkit-transition: opacity 0.6s; + -moz-transition: opacity 0.6s; + -o-transition: opacity 0.6s; + transition: opacity 0.6s; + + &.stop { + @include opacity(0); + } +} diff --git a/web/sass-files/sass/partials/_modal.scss b/web/sass-files/sass/partials/_modal.scss index 5570b5ce437..1dcdbf3489d 100644 --- a/web/sass-files/sass/partials/_modal.scss +++ b/web/sass-files/sass/partials/_modal.scss @@ -228,11 +228,24 @@ background: #FFF; display: table-cell; vertical-align: middle; + position: relative; + + &:hover .file-playback-controls.stop { + @include opacity(1); + } } img { max-width: 100%; max-height: 100%; } + .spinner.file__loading { + z-index: 2; + position: absolute; + left: 50%; + margin-left: -16px; + top: 50%; + margin-top: -16px; + } } .modal-content{ box-shadow: none; diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss index 0f3cc0ef668..6ecc0d96599 100644 --- a/web/sass-files/sass/partials/_post.scss +++ b/web/sass-files/sass/partials/_post.scss @@ -119,20 +119,52 @@ body.ios { background-color: rgba(0, 0, 0, 0.6); text-align: center; color: #FFF; - display: table; - font-size: 1.7em; + font-size: em(20px); font-weight: 600; z-index: 6; - > div { - display: table-cell; - vertical-align: middle; + &.right-file-overlay { + font-size: em(18px); + .overlay__circle { + width: 300px; + height: 300px; + margin: -150px 0 0 -150px; + } + .overlay__files { + margin: 60px auto 15px; + width: 150px; + } + } + + .overlay__circle { + background: #111; + background: rgba(black, 0.7); + width: 370px; + height: 370px; + margin: -185px 0 0 -185px; + @include border-radius(500px); + position: absolute; + top: 50%; + left: 50%; + } + + .overlay__files { + display: block; + margin: 75px auto 20px; + } + + .overlay__logo { + position: absolute; + left: 50%; + bottom: 30px; + margin-left: -50px; + @include opacity(0.3); } .fa { - display: block; - font-size: 2em; - margin: 0 0 0.3em; + display: inline-block; + font-size: 1.1em; + margin-right: 8px; } } diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss index 09ac2047cab..c8bb24f3aa5 100644 --- a/web/sass-files/sass/partials/_responsive.scss +++ b/web/sass-files/sass/partials/_responsive.scss @@ -199,9 +199,6 @@ } @media screen and (max-width: 960px) { - .center-file-overlay { - font-size: 1.5em; - } .post { .post-header .post-header-col.post-header__reply { .comment-icon__container__hide { @@ -278,8 +275,17 @@ display: block; } } - .center-file-overlay { - font-size: 1.3em; + .file-overlay { + font-size: em(18px); + .overlay__circle { + width: 300px; + height: 300px; + margin: -150px 0 0 -150px; + } + .overlay__files { + margin: 60px auto 15px; + width: 150px; + } } .date-separator, .new-separator { &.hovered--after { @@ -639,6 +645,9 @@ } &.has-close { .btn-close { + width: 40px; + text-align: center; + right: 0; display: block; @include opacity(0.5); } @@ -749,6 +758,10 @@ .post-comments { padding: 9px 21px 10px 10px !important; } + + .post-image__column .post__image .file-playback-controls.stop, .image-wrapper > a .file-playback-controls.stop { + @include opacity(1); + } } @media screen and (max-width: 640px) { .access-history__table { diff --git a/web/sass-files/sass/partials/_settings.scss b/web/sass-files/sass/partials/_settings.scss index c4591d7b61e..bc53dc0e477 100644 --- a/web/sass-files/sass/partials/_settings.scss +++ b/web/sass-files/sass/partials/_settings.scss @@ -103,6 +103,9 @@ text-overflow: ellipsis; margin-bottom: 0; } + .input-group-addon { + background: transparent; + } .radio { label { font-weight: 600; diff --git a/web/static/images/filesOverlay.png b/web/static/images/filesOverlay.png new file mode 100644 index 00000000000..d24da762657 Binary files /dev/null and b/web/static/images/filesOverlay.png differ diff --git a/web/static/images/logoWhite.png b/web/static/images/logoWhite.png new file mode 100644 index 00000000000..11bbd463222 Binary files /dev/null and b/web/static/images/logoWhite.png differ