From b1d9af215494d5c5cee6eb092876fe27c4ef4e71 Mon Sep 17 00:00:00 2001 From: Pat Lathem Date: Fri, 16 Oct 2015 15:13:29 -0500 Subject: [PATCH 01/28] Add a /shrug command --- api/command.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/api/command.go b/api/command.go index 94b2cd2f8a..956c8c723a 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" From 58b57acfacbae4aa4af0688ad89ba2f6c7d100ef Mon Sep 17 00:00:00 2001 From: Florian Orben Date: Mon, 19 Oct 2015 03:01:53 +0200 Subject: [PATCH 02/28] PLT-751: Enable arrow to edit posts in comment threads --- web/react/components/create_comment.jsx | 24 ++++++++++++++++++++++++ web/react/stores/post_store.jsx | 13 ++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx index 2df3dc40f3..73d5e27a69 100644 --- a/web/react/components/create_comment.jsx +++ b/web/react/components/create_comment.jsx @@ -13,8 +13,10 @@ const MsgTyping = require('./msg_typing.jsx'); const FileUpload = require('./file_upload.jsx'); const FilePreview = require('./file_preview.jsx'); const Utils = require('../utils/utils.jsx'); + const Constants = require('../utils/constants.jsx'); const ActionTypes = Constants.ActionTypes; +const KeyCodes = Constants.KeyCodes; export default class CreateComment extends React.Component { constructor(props) { @@ -25,6 +27,7 @@ export default class CreateComment extends React.Component { this.handleSubmit = this.handleSubmit.bind(this); this.commentMsgKeyPress = this.commentMsgKeyPress.bind(this); this.handleUserInput = this.handleUserInput.bind(this); + this.handleArrowUp = this.handleArrowUp.bind(this); this.handleUploadStart = this.handleUploadStart.bind(this); this.handleFileUploadComplete = this.handleFileUploadComplete.bind(this); this.handleUploadError = this.handleUploadError.bind(this); @@ -147,6 +150,26 @@ export default class CreateComment extends React.Component { $('.post-right__scroll').perfectScrollbar('update'); this.setState({messageText: messageText}); } + handleArrowUp(e) { + if (e.keyCode === KeyCodes.UP && this.state.messageText === '') { + e.preventDefault(); + + const channelId = ChannelStore.getCurrentId(); + const lastPost = PostStore.getCurrentUsersLatestPost(channelId, this.props.rootId); + if (!lastPost) { + return; + } + + AppDispatcher.handleViewAction({ + type: ActionTypes.RECIEVED_EDIT_POST, + refocusId: '#reply_textbox', + title: 'Comment', + message: lastPost.message, + postId: lastPost.id, + channelId: lastPost.channel_id + }); + } + } handleUploadStart(clientIds) { let draft = PostStore.getCommentDraft(this.props.rootId); @@ -279,6 +302,7 @@ export default class CreateComment extends React.Component { Date: Mon, 19 Oct 2015 21:06:09 +0200 Subject: [PATCH 03/28] Allow to update root comment in RHS via arrow up shortcut --- web/react/stores/post_store.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/react/stores/post_store.jsx b/web/react/stores/post_store.jsx index 90adc408dc..0ace956d2d 100644 --- a/web/react/stores/post_store.jsx +++ b/web/react/stores/post_store.jsx @@ -240,7 +240,7 @@ class PostStoreClass extends EventEmitter { for (i; i < len; i++) { if (postList.posts[postList.order[i]].user_id === userId) { if (rootId) { - if (postList.posts[postList.order[i]].root_id === rootId) { + if (postList.posts[postList.order[i]].root_id === rootId || postList.posts[postList.order[i]].id === rootId) { lastPost = postList.posts[postList.order[i]]; break; } From e48614d6cec19f781a63bc5166d7cc286c281d7b Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Tue, 20 Oct 2015 18:31:34 +0500 Subject: [PATCH 04/28] PLT-318 - Updating files overlay design --- web/react/components/file_upload_overlay.jsx | 16 ++++-- web/sass-files/sass/partials/_post.scss | 48 +++++++++++++++--- web/sass-files/sass/partials/_responsive.scss | 16 ++++-- web/static/images/filesOverlay.png | Bin 0 -> 8392 bytes web/static/images/logo-email copy.png | Bin 0 -> 10380 bytes web/static/images/logoWhite.png | Bin 0 -> 5876 bytes 6 files changed, 64 insertions(+), 16 deletions(-) create mode 100644 web/static/images/filesOverlay.png create mode 100644 web/static/images/logo-email copy.png create mode 100644 web/static/images/logoWhite.png diff --git a/web/react/components/file_upload_overlay.jsx b/web/react/components/file_upload_overlay.jsx index 4fcee6cb05..d991dd6252 100644 --- a/web/react/components/file_upload_overlay.jsx +++ b/web/react/components/file_upload_overlay.jsx @@ -12,9 +12,19 @@ export default class FileUploadOverlay extends React.Component { return (
-
- - Drop a file to upload it. +
+ Files + {'Drop a file to upload it.'} + Logo
); diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss index 0f3cc0ef66..6ecc0d9659 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 09ac2047ca..71ae526359 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 { diff --git a/web/static/images/filesOverlay.png b/web/static/images/filesOverlay.png new file mode 100644 index 0000000000000000000000000000000000000000..d24da7626572cf7de0ad1289b35291f614a82a44 GIT binary patch literal 8392 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 literal 0 HcmV?d00001 diff --git a/web/static/images/logo-email copy.png b/web/static/images/logo-email copy.png new file mode 100644 index 0000000000000000000000000000000000000000..c16978ba895a3564239aa72f6ac4d295fef97e42 GIT binary patch literal 10380 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 N5GgS^(PEgs&;QHC35fsz literal 0 HcmV?d00001 diff --git a/web/static/images/logoWhite.png b/web/static/images/logoWhite.png new file mode 100644 index 0000000000000000000000000000000000000000..11bbd46322249a039750a097e43eabd71747d1a0 GIT binary patch literal 5876 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 KMNUMnLSTaNkVFmu literal 0 HcmV?d00001 From 4ba3a19855170fae8d29511aa82561f3975da2c0 Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Tue, 20 Oct 2015 18:34:49 +0500 Subject: [PATCH 05/28] Adding background transparent for theme inputs --- web/sass-files/sass/partials/_settings.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/sass-files/sass/partials/_settings.scss b/web/sass-files/sass/partials/_settings.scss index c4591d7b61..5fc9b295b5 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:not(:first-child) { + background: transparent; + } .radio { label { font-weight: 600; From de3cd15b23641109af290be7623abc176dcc4a60 Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Tue, 20 Oct 2015 18:36:33 +0500 Subject: [PATCH 06/28] Updating custom themes input addon --- web/sass-files/sass/partials/_settings.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/sass-files/sass/partials/_settings.scss b/web/sass-files/sass/partials/_settings.scss index 5fc9b295b5..bc53dc0e47 100644 --- a/web/sass-files/sass/partials/_settings.scss +++ b/web/sass-files/sass/partials/_settings.scss @@ -103,7 +103,7 @@ text-overflow: ellipsis; margin-bottom: 0; } - .input-group-addon:not(:first-child) { + .input-group-addon { background: transparent; } .radio { From b28b519d32030650f3c42946e40b59da8d5dbb83 Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Tue, 20 Oct 2015 20:56:30 +0500 Subject: [PATCH 07/28] Removing logo email copy file --- web/static/images/logo-email copy.png | Bin 10380 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 web/static/images/logo-email copy.png diff --git a/web/static/images/logo-email copy.png b/web/static/images/logo-email copy.png deleted file mode 100644 index c16978ba895a3564239aa72f6ac4d295fef97e42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10380 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 N5GgS^(PEgs&;QHC35fsz From e9fc2434fc98bf3ac9ae7b0f05609169f19d07ea Mon Sep 17 00:00:00 2001 From: Asaad Mahmood Date: Tue, 20 Oct 2015 21:08:18 +0500 Subject: [PATCH 08/28] Updating btn-close size for direct messages on mobile devices. --- web/sass-files/sass/partials/_responsive.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss index 71ae526359..3bffe82a22 100644 --- a/web/sass-files/sass/partials/_responsive.scss +++ b/web/sass-files/sass/partials/_responsive.scss @@ -645,6 +645,9 @@ } &.has-close { .btn-close { + width: 40px; + text-align: center; + right: 0; display: block; @include opacity(0.5); } From bfb3040d552a49fb718ea4f5d1f417d42dd11dca Mon Sep 17 00:00:00 2001 From: it33 Date: Tue, 20 Oct 2015 11:38:39 -0700 Subject: [PATCH 09/28] Minor formatting adjustments --- CHANGELOG.md | 91 ++++++++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49a0ead894..54d9122c00 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 From b9ce4a644d8e97ed05acb79033ea2f5043494ee4 Mon Sep 17 00:00:00 2001 From: =Corey Hulen Date: Tue, 20 Oct 2015 17:30:24 -0700 Subject: [PATCH 10/28] PLT-350 allow ability to disable restricted team names --- api/team.go | 9 ++--- config/config.json | 3 +- model/config.go | 6 ++++ model/team.go | 4 +-- store/sql_team_store.go | 5 +-- utils/config.go | 1 + .../admin_console/team_settings.jsx | 34 +++++++++++++++++++ web/react/components/team_signup_url_page.jsx | 10 +++--- 8 files changed, 56 insertions(+), 16 deletions(-) diff --git a/api/team.go b/api/team.go index f6038566a8..2d7b05ff60 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 37109428da..7bac58df7b 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/model/config.go b/model/config.go index 3a39df2f19..216b1de860 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 584c78f8d8..9da2cd5b2a 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/store/sql_team_store.go b/store/sql_team_store.go index 2d65435b0b..380d979bd3 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 e3349650b9..15d6b217c8 100644 --- a/utils/config.go +++ b/utils/config.go @@ -182,6 +182,7 @@ func getClientProperties(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 da4299714a..9ecd14a1e9 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'}

+
+
+
{serverError} diff --git a/web/react/components/team_signup_url_page.jsx b/web/react/components/team_signup_url_page.jsx index 67e4c9dd7a..75ec2dfd90 100644 --- a/web/react/components/team_signup_url_page.jsx +++ b/web/react/components/team_signup_url_page.jsx @@ -40,10 +40,12 @@ export default class TeamSignupUrlPage extends React.Component { return; } - for (let index = 0; index < Constants.RESERVED_TEAM_NAMES.length; index++) { - if (cleanedName.indexOf(Constants.RESERVED_TEAM_NAMES[index]) === 0) { - this.setState({nameError: 'URL is taken or contains a reserved word'}); - return; + if (global.window.config.RestrictTeamNames === 'true') { + for (let index = 0; index < Constants.RESERVED_TEAM_NAMES.length; index++) { + if (cleanedName.indexOf(Constants.RESERVED_TEAM_NAMES[index]) === 0) { + this.setState({nameError: 'URL is taken or contains a reserved word'}); + return; + } } } From 2022ed68f39ab777e113248d501d36c44c8976b7 Mon Sep 17 00:00:00 2001 From: =Corey Hulen Date: Tue, 20 Oct 2015 20:39:06 -0700 Subject: [PATCH 11/28] Fixing unit test --- model/team_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/model/team_test.go b/model/team_test.go index fd2428f03a..10592c657b 100644 --- a/model/team_test.go +++ b/model/team_test.go @@ -21,12 +21,12 @@ 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") } @@ -53,13 +53,13 @@ func TestTeamIsValid(t *testing.T) { 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) } } From 0bfdfd79f0071cfd41f2dfbe20d1cb099b57a15c Mon Sep 17 00:00:00 2001 From: =Corey Hulen Date: Tue, 20 Oct 2015 21:15:06 -0700 Subject: [PATCH 12/28] Fixing unit tests --- model/team_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/model/team_test.go b/model/team_test.go index 10592c657b..112d48a9da 100644 --- a/model/team_test.go +++ b/model/team_test.go @@ -31,23 +31,23 @@ func TestTeamIsValid(t *testing.T) { } 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") } From 9553e44dc55e1798180a942e774e9b11f8b01d67 Mon Sep 17 00:00:00 2001 From: it33 Date: Wed, 21 Oct 2015 08:47:09 -0700 Subject: [PATCH 13/28] Create Search.md --- doc/help/Search.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 doc/help/Search.md diff --git a/doc/help/Search.md b/doc/help/Search.md new file mode 100644 index 0000000000..f36e079bdc --- /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. From d167e18f0048344c54685a08c19113a84a995ed9 Mon Sep 17 00:00:00 2001 From: Florian Orben Date: Sat, 17 Oct 2015 01:58:34 +0200 Subject: [PATCH 14/28] PLT-616: Enable playing of animated GIF in thumbnails and preview --- api/file.go | 10 ++++ web/react/components/file_attachment.jsx | 53 +++++++++++++++++- web/react/components/view_image.jsx | 70 +++++++++++++++++++++++- web/react/utils/utils.jsx | 1 + web/sass-files/sass/partials/_files.scss | 22 ++++++++ web/sass-files/sass/partials/_modal.scss | 9 +++ 6 files changed, 161 insertions(+), 4 deletions(-) diff --git a/api/file.go b/api/file.go index 142ef7ac77..94eea516ad 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/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx index c6dff65507..8178343340 100644 --- a/web/react/components/file_attachment.jsx +++ b/web/react/components/file_attachment.jsx @@ -10,9 +10,10 @@ export default class FileAttachment extends React.Component { super(props); this.loadFiles = this.loadFiles.bind(this); + this.playGif = this.playGif.bind(this); this.canSetState = false; - this.state = {fileSize: -1}; + this.state = {fileSize: -1, mime: '', playing: false, loading: false}; } componentDidMount() { this.loadFiles(); @@ -93,6 +94,16 @@ export default class FileAttachment extends React.Component { return true; } + playGif(e, fileUrl) { + var img = new Image(); + + this.setState({loading: true}); + img.load(fileUrl); + img.onload = () => this.setState({playing: true, loading: false}); + img.onError = () => this.setState({loading: false}); + + e.stopPropagation(); + } render() { var filename = this.props.filename; @@ -100,6 +111,38 @@ export default class FileAttachment extends React.Component { var fileUrl = utils.getFileUrl(filename); var type = utils.getFileType(fileInfo.ext); + var playButton = ''; + var loadedFile = ''; + var loadingIndicator = ''; + if (this.state.mime === 'image/gif') { + playButton = ( +
this.playGif(e, fileUrl)} + > + {"►"} +
+ ); + } + if (this.state.playing) { + loadedFile = ( + + ); + playButton = ''; + } + if (this.state.loading) { + loadingIndicator = ( + + ); + playButton = ''; + } + var thumbnail; if (type === 'image') { thumbnail = ( @@ -107,7 +150,11 @@ export default class FileAttachment extends React.Component { ref={filename} className='post__load' style={{backgroundImage: 'url(/static/images/load.gif)'}} - /> + > + {loadingIndicator} + {playButton} + {loadedFile} +
); } else { thumbnail =
; @@ -119,7 +166,7 @@ export default class FileAttachment extends React.Component { filename, function success(data) { if (this.canSetState) { - this.setState({fileSize: parseInt(data.size, 10)}); + this.setState({fileSize: parseInt(data.size, 10), mime: data.mime}); } }.bind(this), function error() {} diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx index 322e68c173..c8356b2fa3 100644 --- a/web/react/components/view_image.jsx +++ b/web/react/components/view_image.jsx @@ -38,7 +38,10 @@ export default class ViewImageModal extends React.Component { progress: progress, images: {}, fileSizes: {}, - showFooter: false + fileMimes: {}, + showFooter: false, + isPlaying: {}, + isLoading: {} }; } handleNext(e) { @@ -122,6 +125,28 @@ export default class ViewImageModal extends React.Component { this.setState({loaded}); } } + playGif(e, filename, fileUrl) { + var isLoading = this.state.isLoading; + var isPlaying = this.state.isPlaying; + + isLoading[filename] = fileUrl; + this.setState({isLoading}); + + var img = new Image(); + img.load(fileUrl); + img.onload = () => { + delete isLoading[filename]; + isPlaying[filename] = fileUrl; + this.setState({isPlaying, isLoading}); + }; + img.onError = () => { + delete isLoading[filename]; + this.setState({isLoading}); + }; + + e.stopPropagation(); + e.preventDefault(); + } componentDidMount() { $(window).on('keyup', this.handleKeyPress); @@ -154,6 +179,10 @@ export default class ViewImageModal extends React.Component { var fileType = Utils.getFileType(fileInfo.ext); if (fileType === 'image') { + if (typeof this.state.isPlaying[filename] !== 'undefined') { + return this.state.isPlaying[filename]; + } + // This is a temporary patch to fix issue with old files using absolute paths if (fileInfo.path.indexOf('/api/v1/files/get') !== -1) { fileInfo.path = fileInfo.path.split('/api/v1/files/get')[1]; @@ -189,12 +218,51 @@ export default class ViewImageModal extends React.Component { var fileType = Utils.getFileType(fileInfo.ext); if (fileType === 'image') { + if (!(filename in this.state.fileMimes)) { + Client.getFileInfo( + filename, + (data) => { + if (this.canSetState) { + var fileMimes = this.state.fileMimes; + fileMimes[filename] = data.mime; + this.setState(fileMimes); + } + }, + () => {} + ); + } + + var playButton = ''; + if (this.state.fileMimes[filename] === 'image/gif' && !(filename in this.state.isLoading) && !(filename in this.state.isPlaying)) { + playButton = ( +
this.playGif(e, filename, fileUrl)} + > + {"►"} +
+ ); + } + + var loadingIndicator = ''; + if (this.state.isLoading[filename] === fileUrl) { + loadingIndicator = ( + + ); + playButton = ''; + } + // image files just show a preview of the file content = ( + {loadingIndicator} + {playButton} Date: Mon, 19 Oct 2015 21:15:10 +0200 Subject: [PATCH 15/28] Horizontally align images if image height > image width --- web/react/components/file_attachment.jsx | 18 +++++++++++++++--- web/sass-files/sass/partials/_files.scss | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx index 8178343340..afd4e32fed 100644 --- a/web/react/components/file_attachment.jsx +++ b/web/react/components/file_attachment.jsx @@ -94,16 +94,28 @@ export default class FileAttachment extends React.Component { return true; } - playGif(e, fileUrl) { + playGif(e, filename) { var img = new Image(); + var fileUrl = utils.getFileUrl(filename); this.setState({loading: true}); img.load(fileUrl); - img.onload = () => this.setState({playing: true, loading: false}); + img.onload = () => { + this.setState({playing: true, loading: false}); + + // keep displaying background image for a short moment while browser is + // loading gif, to prevent white background flashing through + setTimeout(() => this.removeBackgroundImage.bind(this)(filename), 100); + }; img.onError = () => this.setState({loading: false}); e.stopPropagation(); } + removeBackgroundImage(name) { + if (name in this.refs) { + $(ReactDOM.findDOMNode(this.refs[name])).css('background-image', 'initial'); + } + } render() { var filename = this.props.filename; @@ -118,7 +130,7 @@ export default class FileAttachment extends React.Component { playButton = (
this.playGif(e, fileUrl)} + onClick={(e) => this.playGif(e, filename)} > {"►"}
diff --git a/web/sass-files/sass/partials/_files.scss b/web/sass-files/sass/partials/_files.scss index 1d7754445c..cb9ff6862b 100644 --- a/web/sass-files/sass/partials/_files.scss +++ b/web/sass-files/sass/partials/_files.scss @@ -135,6 +135,7 @@ background-repeat: no-repeat; overflow: hidden; position: relative; + text-align: center; &.small { background-position: center; } From cd5df514c7d3130c9765a8160dfd31abccb8b741 Mon Sep 17 00:00:00 2001 From: Florian Orben Date: Mon, 19 Oct 2015 22:30:00 +0200 Subject: [PATCH 16/28] Add stop button to stop animated gifs --- web/react/components/file_attachment.jsx | 101 ++++++++++++++++------- web/react/components/view_image.jsx | 45 +++++++--- web/react/utils/utils.jsx | 2 +- web/sass-files/sass/partials/_files.scss | 15 +++- web/sass-files/sass/partials/_modal.scss | 4 + 5 files changed, 123 insertions(+), 44 deletions(-) diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx index afd4e32fed..a88731ca96 100644 --- a/web/react/components/file_attachment.jsx +++ b/web/react/components/file_attachment.jsx @@ -11,6 +11,8 @@ export default class FileAttachment extends React.Component { this.loadFiles = this.loadFiles.bind(this); this.playGif = this.playGif.bind(this); + this.stopGif = this.stopGif.bind(this); + this.addBackgroundImage = this.addBackgroundImage.bind(this); this.canSetState = false; this.state = {fileSize: -1, mime: '', playing: false, loading: false}; @@ -29,15 +31,9 @@ export default class FileAttachment extends React.Component { var filename = this.props.filename; if (filename) { - var fileInfo = utils.splitFileLocation(filename); + var fileInfo = this.getFileInfoFromName(filename); var type = utils.getFileType(fileInfo.ext); - // This is a temporary patch to fix issue with old files using absolute paths - if (fileInfo.path.indexOf('/api/v1/files/get') !== -1) { - fileInfo.path = fileInfo.path.split('/api/v1/files/get')[1]; - } - fileInfo.path = utils.getWindowLocationOrigin() + '/api/v1/files/get' + fileInfo.path; - if (type === 'image') { var self = this; // Need this reference since we use the given "this" $('').attr('src', fileInfo.path + '_thumb.jpg').load(function loadWrapper(path, name) { @@ -59,11 +55,7 @@ export default class FileAttachment extends React.Component { $(imgDiv).addClass('normal'); } - var re1 = new RegExp(' ', 'g'); - var re2 = new RegExp('\\(', 'g'); - var re3 = new RegExp('\\)', 'g'); - var url = path.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29'); - $(imgDiv).css('background-image', 'url(' + url + '_thumb.jpg)'); + self.addBackgroundImage(name, path); } }; }(fileInfo.path, filename)); @@ -111,6 +103,39 @@ export default class FileAttachment extends React.Component { e.stopPropagation(); } + stopGif(e, filename) { + this.setState({playing: false}); + this.addBackgroundImage(filename); + e.stopPropagation(); + } + getFileInfoFromName(name) { + var fileInfo = utils.splitFileLocation(name); + + // This is a temporary patch to fix issue with old files using absolute paths + if (fileInfo.path.indexOf('/api/v1/files/get') !== -1) { + fileInfo.path = fileInfo.path.split('/api/v1/files/get')[1]; + } + fileInfo.path = utils.getWindowLocationOrigin() + '/api/v1/files/get' + fileInfo.path; + + return fileInfo; + } + addBackgroundImage(name, path) { + var fileUrl = path; + + if (name in this.refs) { + if (!path) { + fileUrl = this.getFileInfoFromName(name).path; + } + + var imgDiv = ReactDOM.findDOMNode(this.refs[name]); + var re1 = new RegExp(' ', 'g'); + var re2 = new RegExp('\\(', 'g'); + var re3 = new RegExp('\\)', 'g'); + var url = fileUrl.replace(re1, '%20').replace(re2, '%28').replace(re3, '%29'); + + $(imgDiv).css('background-image', 'url(' + url + '_thumb.jpg)'); + } + } removeBackgroundImage(name) { if (name in this.refs) { $(ReactDOM.findDOMNode(this.refs[name])).css('background-image', 'initial'); @@ -123,13 +148,13 @@ export default class FileAttachment extends React.Component { var fileUrl = utils.getFileUrl(filename); var type = utils.getFileType(fileInfo.ext); - var playButton = ''; + var playbackControls = ''; var loadedFile = ''; var loadingIndicator = ''; if (this.state.mime === 'image/gif') { - playButton = ( + playbackControls = (
this.playGif(e, filename)} > {"►"} @@ -143,7 +168,14 @@ export default class FileAttachment extends React.Component { src={fileUrl} /> ); - playButton = ''; + playbackControls = ( +
this.stopGif(e, filename)} + > + {"◼"} +
+ ); } if (this.state.loading) { loadingIndicator = ( @@ -152,22 +184,35 @@ export default class FileAttachment extends React.Component { src='/static/images/load.gif' /> ); - playButton = ''; + playbackControls = ''; } var thumbnail; if (type === 'image') { - thumbnail = ( -
- {loadingIndicator} - {playButton} - {loadedFile} -
- ); + if (this.state.playing) { + thumbnail = ( +
+ {playbackControls} + {loadedFile} +
+ ); + } else { + thumbnail = ( +
+ {loadingIndicator} + {playbackControls} + {loadedFile} +
+ ); + } } else { thumbnail =
; } diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx index c8356b2fa3..fbe32cb07a 100644 --- a/web/react/components/view_image.jsx +++ b/web/react/components/view_image.jsx @@ -147,6 +147,14 @@ export default class ViewImageModal extends React.Component { e.stopPropagation(); e.preventDefault(); } + stopGif(e, filename) { + var isPlaying = this.state.isPlaying; + delete isPlaying[filename]; + this.setState({isPlaying}); + + e.stopPropagation(); + e.preventDefault(); + } componentDidMount() { $(window).on('keyup', this.handleKeyPress); @@ -179,7 +187,7 @@ export default class ViewImageModal extends React.Component { var fileType = Utils.getFileType(fileInfo.ext); if (fileType === 'image') { - if (typeof this.state.isPlaying[filename] !== 'undefined') { + if (filename in this.state.isPlaying) { return this.state.isPlaying[filename]; } @@ -232,16 +240,27 @@ export default class ViewImageModal extends React.Component { ); } - var playButton = ''; - if (this.state.fileMimes[filename] === 'image/gif' && !(filename in this.state.isLoading) && !(filename in this.state.isPlaying)) { - playButton = ( -
this.playGif(e, filename, fileUrl)} - > - {"►"} -
- ); + var playbackControls = ''; + if (this.state.fileMimes[filename] === 'image/gif' && !(filename in this.state.isLoading)) { + if (filename in this.state.isPlaying) { + playbackControls = ( +
this.stopGif(e, filename)} + > + {"◼"} +
+ ); + } else { + playbackControls = ( +
this.playGif(e, filename, fileUrl)} + > + {"►"} +
+ ); + } } var loadingIndicator = ''; @@ -252,7 +271,7 @@ export default class ViewImageModal extends React.Component { src='/static/images/load.gif' /> ); - playButton = ''; + playbackControls = ''; } // image files just show a preview of the file @@ -262,7 +281,7 @@ export default class ViewImageModal extends React.Component { target='_blank' > {loadingIndicator} - {playButton} + {playbackControls} Date: Mon, 19 Oct 2015 22:48:15 +0200 Subject: [PATCH 17/28] always show stop gif button on small screen sizes --- web/sass-files/sass/partials/_responsive.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss index 3bffe82a22..c8bb24f3aa 100644 --- a/web/sass-files/sass/partials/_responsive.scss +++ b/web/sass-files/sass/partials/_responsive.scss @@ -758,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 { From b00697a5009b30eea7e34f340dfdf6b691c4087b Mon Sep 17 00:00:00 2001 From: Florian Orben Date: Tue, 20 Oct 2015 20:42:02 +0200 Subject: [PATCH 18/28] apply portrait/landscape/quadrat format to playing gif to always display it in best possible way --- web/react/components/file_attachment.jsx | 20 +++++++++++++++++--- web/sass-files/sass/partials/_files.scss | 7 ++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx index a88731ca96..a6957b06b9 100644 --- a/web/react/components/file_attachment.jsx +++ b/web/react/components/file_attachment.jsx @@ -15,7 +15,7 @@ export default class FileAttachment extends React.Component { this.addBackgroundImage = this.addBackgroundImage.bind(this); this.canSetState = false; - this.state = {fileSize: -1, mime: '', playing: false, loading: false}; + this.state = {fileSize: -1, mime: '', playing: false, loading: false, format: ''}; } componentDidMount() { this.loadFiles(); @@ -93,7 +93,21 @@ export default class FileAttachment extends React.Component { this.setState({loading: true}); img.load(fileUrl); img.onload = () => { - this.setState({playing: true, loading: false}); + var state = {playing: true, loading: false}; + + switch (true) { + case img.width > img.height: + state.format = 'landscape'; + break; + case img.height > img.width: + state.format = 'portrait'; + break; + default: + state.format = 'quadrat'; + break; + } + + this.setState(state); // keep displaying background image for a short moment while browser is // loading gif, to prevent white background flashing through @@ -164,7 +178,7 @@ export default class FileAttachment extends React.Component { if (this.state.playing) { loadedFile = ( ); diff --git a/web/sass-files/sass/partials/_files.scss b/web/sass-files/sass/partials/_files.scss index e4d6582300..d3ab3b9f84 100644 --- a/web/sass-files/sass/partials/_files.scss +++ b/web/sass-files/sass/partials/_files.scss @@ -150,8 +150,13 @@ margin-top: -16px; } .file__loaded { - height: 100px; max-width: initial; + &.landscape, &.quadrat { + height: 100px; + } + &.portrait { + width: 120px; + } } &:hover .file-playback-controls.stop { @include opacity(1); From b9c637ec82d28684029573af119606575f4978d0 Mon Sep 17 00:00:00 2001 From: Florian Orben Date: Wed, 21 Oct 2015 18:15:04 +0200 Subject: [PATCH 19/28] Change stop symbol to another one since chrome-win can't display it --- web/react/components/file_attachment.jsx | 2 +- web/react/components/view_image.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx index a6957b06b9..57cccc4e0f 100644 --- a/web/react/components/file_attachment.jsx +++ b/web/react/components/file_attachment.jsx @@ -187,7 +187,7 @@ export default class FileAttachment extends React.Component { className='file-playback-controls stop' onClick={(e) => this.stopGif(e, filename)} > - {"◼"} + {"■"}
); } diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx index fbe32cb07a..bea6ce7a55 100644 --- a/web/react/components/view_image.jsx +++ b/web/react/components/view_image.jsx @@ -248,7 +248,7 @@ export default class ViewImageModal extends React.Component { className='file-playback-controls stop' onClick={(e) => this.stopGif(e, filename)} > - {"◼"} + {"■"}
); } else { From 7e24890bd37e62ec9992452751c203430cdc3ea4 Mon Sep 17 00:00:00 2001 From: Pat Lathem Date: Wed, 21 Oct 2015 14:39:33 -0500 Subject: [PATCH 20/28] Add a call to doFormatText inside of the markdown paragraph handler --- web/react/utils/markdown.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/react/utils/markdown.jsx b/web/react/utils/markdown.jsx index 2813798d22..04f0bfc98f 100644 --- a/web/react/utils/markdown.jsx +++ b/web/react/utils/markdown.jsx @@ -3,6 +3,7 @@ const TextFormatting = require('./text_formatting.jsx'); const Utils = require('./utils.jsx'); +const Emoticons = require('./emoticons.jsx'); const marked = require('marked'); @@ -53,6 +54,8 @@ export class MattermostMarkdownRenderer extends marked.Renderer { } paragraph(text) { + text = TextFormatting.doFormatText(text, this.options) + if (this.formattingOptions.singleline) { return `

${text}

`; } From 9dd7a5e01e08da947b638a9c89c872a28e8f56df Mon Sep 17 00:00:00 2001 From: Pat Lathem Date: Wed, 21 Oct 2015 14:44:22 -0500 Subject: [PATCH 21/28] Remove stray emoticons.jsx import --- web/react/utils/markdown.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/web/react/utils/markdown.jsx b/web/react/utils/markdown.jsx index 04f0bfc98f..1c3d498a93 100644 --- a/web/react/utils/markdown.jsx +++ b/web/react/utils/markdown.jsx @@ -3,7 +3,6 @@ const TextFormatting = require('./text_formatting.jsx'); const Utils = require('./utils.jsx'); -const Emoticons = require('./emoticons.jsx'); const marked = require('marked'); From 8eb2c0266c562d43805cb5a30a04102c7e728924 Mon Sep 17 00:00:00 2001 From: Pat Lathem Date: Wed, 21 Oct 2015 14:52:19 -0500 Subject: [PATCH 22/28] Missing semicolon --- web/react/utils/markdown.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/react/utils/markdown.jsx b/web/react/utils/markdown.jsx index 1c3d498a93..e8e5798bdf 100644 --- a/web/react/utils/markdown.jsx +++ b/web/react/utils/markdown.jsx @@ -53,7 +53,7 @@ export class MattermostMarkdownRenderer extends marked.Renderer { } paragraph(text) { - text = TextFormatting.doFormatText(text, this.options) + text = TextFormatting.doFormatText(text, this.options); if (this.formattingOptions.singleline) { return `

${text}

`; From fba89915ac9a0421a5441cdb23958099467a0a56 Mon Sep 17 00:00:00 2001 From: Pat Lathem Date: Wed, 21 Oct 2015 15:02:23 -0500 Subject: [PATCH 23/28] Pass eslint tests --- web/react/utils/markdown.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/react/utils/markdown.jsx b/web/react/utils/markdown.jsx index e8e5798bdf..1b699bc982 100644 --- a/web/react/utils/markdown.jsx +++ b/web/react/utils/markdown.jsx @@ -53,13 +53,13 @@ export class MattermostMarkdownRenderer extends marked.Renderer { } paragraph(text) { - text = TextFormatting.doFormatText(text, this.options); + let outText = TextFormatting.doFormatText(text, this.options); if (this.formattingOptions.singleline) { - return `

${text}

`; + return `

${outText}

`; } - return super.paragraph(text); + return super.paragraph(outText); } table(header, body) { From 01a2ad2ccf8ad2fb2fa028732ce28b2d0f9db701 Mon Sep 17 00:00:00 2001 From: it33 Date: Wed, 21 Oct 2015 14:52:55 -0700 Subject: [PATCH 24/28] Update Upgrade-Guide.md --- doc/install/Upgrade-Guide.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/install/Upgrade-Guide.md b/doc/install/Upgrade-Guide.md index e86cf81662..1845f3ad35 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. From d8f6cb939f3f931d74833deedc17b29d807ad035 Mon Sep 17 00:00:00 2001 From: it33 Date: Wed, 21 Oct 2015 14:53:22 -0700 Subject: [PATCH 25/28] Update Upgrade-Guide.md --- doc/install/Upgrade-Guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/install/Upgrade-Guide.md b/doc/install/Upgrade-Guide.md index 1845f3ad35..cecd45353f 100644 --- a/doc/install/Upgrade-Guide.md +++ b/doc/install/Upgrade-Guide.md @@ -2,7 +2,7 @@ ### Upgrading Mattermost v0.7 to v1.1.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._ +_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: From 74402df462a0efa0b015792ee8c77a16dc72bedb Mon Sep 17 00:00:00 2001 From: hmhealey Date: Thu, 22 Oct 2015 09:47:04 -0400 Subject: [PATCH 26/28] Fixed improperly quoted html attributes for emoticons --- web/react/utils/emoticons.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/react/utils/emoticons.jsx b/web/react/utils/emoticons.jsx index 7b43e48b4c..aabddcffdc 100644 --- a/web/react/utils/emoticons.jsx +++ b/web/react/utils/emoticons.jsx @@ -133,7 +133,7 @@ export function handleEmoticons(text, tokens) { const alias = `MM_EMOTICON${index}`; tokens.set(alias, { - value: `${match}`, + value: `${match}`, originalText: match }); From 25e018ece082a8136024b6d0a6c9db46cb113db1 Mon Sep 17 00:00:00 2001 From: hmhealey Date: Thu, 22 Oct 2015 09:47:28 -0400 Subject: [PATCH 27/28] Changed markdown renderer to only parse emoticons twice --- web/react/utils/markdown.jsx | 7 ++++++- web/react/utils/text_formatting.jsx | 9 +++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/web/react/utils/markdown.jsx b/web/react/utils/markdown.jsx index 1b699bc982..7a4e70054c 100644 --- a/web/react/utils/markdown.jsx +++ b/web/react/utils/markdown.jsx @@ -11,6 +11,7 @@ export class MattermostMarkdownRenderer extends marked.Renderer { super(options); this.heading = this.heading.bind(this); + this.paragraph = this.paragraph.bind(this); this.text = this.text.bind(this); this.formattingOptions = formattingOptions; @@ -53,7 +54,11 @@ export class MattermostMarkdownRenderer extends marked.Renderer { } paragraph(text) { - let outText = TextFormatting.doFormatText(text, this.options); + let outText = text; + + if (!('emoticons' in this.options) || this.options.emoticon) { + outText = TextFormatting.doFormatEmoticons(text); + } if (this.formattingOptions.singleline) { return `

${outText}

`; diff --git a/web/react/utils/text_formatting.jsx b/web/react/utils/text_formatting.jsx index d79aeed68c..5c2e68f1e6 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; From 649f42e3fc706f8fa829276bcdb825381bc703f2 Mon Sep 17 00:00:00 2001 From: it33 Date: Thu, 22 Oct 2015 08:06:45 -0700 Subject: [PATCH 28/28] Update Troubleshooting.md --- doc/install/Troubleshooting.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/doc/install/Troubleshooting.md b/doc/install/Troubleshooting.md index 6a7260ddfa..21839c86f4 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"` +