diff options
-rw-r--r-- | .travis.yml | 2 | ||||
-rw-r--r-- | docker/1.1/Dockerrun.aws.zip | bin | 710 -> 0 bytes | |||
-rw-r--r-- | docker/1.3/Dockerfile (renamed from docker/1.1/Dockerfile) | 2 | ||||
-rw-r--r-- | docker/1.3/Dockerrun.aws.zip | bin | 0 -> 867 bytes | |||
-rw-r--r-- | docker/1.3/Dockerrun.aws/.ebextensions/01_files.config (renamed from docker/1.1/Dockerrun.aws/.ebextensions/01_files.config) | 0 | ||||
-rwxr-xr-x | docker/1.3/Dockerrun.aws/Dockerrun.aws.json (renamed from docker/1.1/Dockerrun.aws/Dockerrun.aws.json) | 2 | ||||
-rw-r--r-- | docker/1.3/README.md | 23 | ||||
-rw-r--r-- | docker/1.3/config_docker.json (renamed from docker/1.1/config_docker.json) | 12 | ||||
-rwxr-xr-x | docker/1.3/docker-entry.sh (renamed from docker/1.1/docker-entry.sh) | 0 | ||||
-rw-r--r-- | model/version.go | 1 | ||||
-rw-r--r-- | store/sql_post_store.go | 2 | ||||
-rw-r--r-- | web/react/components/post.jsx | 2 | ||||
-rw-r--r-- | web/react/stores/post_store.jsx | 121 | ||||
-rw-r--r-- | web/react/stores/socket_store.jsx | 8 | ||||
-rw-r--r-- | web/react/utils/async_client.jsx | 15 | ||||
-rw-r--r-- | web/react/utils/utils.jsx | 5 |
16 files changed, 138 insertions, 57 deletions
diff --git a/.travis.yml b/.travis.yml index 7e54d3335..c88267206 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,8 @@ addons: - 127.0.0.1 dockerhost before_deploy: - sudo rm -rf dist/mattermost + - sudo chown `whoami` dist/mattermost.tar.gz + - sudo chmod 777 dist/mattermost.tar.gz - rvm 1.9.3 do gem install mime-types -v 2.6.2 deploy: # Github releases, builds only on tags diff --git a/docker/1.1/Dockerrun.aws.zip b/docker/1.1/Dockerrun.aws.zip Binary files differdeleted file mode 100644 index 945168a71..000000000 --- a/docker/1.1/Dockerrun.aws.zip +++ /dev/null diff --git a/docker/1.1/Dockerfile b/docker/1.3/Dockerfile index 92bac6a04..55b69673a 100644 --- a/docker/1.1/Dockerfile +++ b/docker/1.3/Dockerfile @@ -34,7 +34,7 @@ VOLUME /var/lib/mysql WORKDIR /mattermost # Copy over files -ADD https://github.com/mattermost/platform/releases/download/v1.1.0/mattermost.tar.gz / +ADD https://github.com/mattermost/platform/releases/download/v1.3.0-rc1/mattermost.tar.gz / RUN tar -zxvf /mattermost.tar.gz --strip-components=1 && rm /mattermost.tar.gz ADD config_docker.json / ADD docker-entry.sh / diff --git a/docker/1.3/Dockerrun.aws.zip b/docker/1.3/Dockerrun.aws.zip Binary files differnew file mode 100644 index 000000000..dd201d990 --- /dev/null +++ b/docker/1.3/Dockerrun.aws.zip diff --git a/docker/1.1/Dockerrun.aws/.ebextensions/01_files.config b/docker/1.3/Dockerrun.aws/.ebextensions/01_files.config index 7f40a8b34..7f40a8b34 100644 --- a/docker/1.1/Dockerrun.aws/.ebextensions/01_files.config +++ b/docker/1.3/Dockerrun.aws/.ebextensions/01_files.config diff --git a/docker/1.1/Dockerrun.aws/Dockerrun.aws.json b/docker/1.3/Dockerrun.aws/Dockerrun.aws.json index 042e79bd3..d4027e67c 100755 --- a/docker/1.1/Dockerrun.aws/Dockerrun.aws.json +++ b/docker/1.3/Dockerrun.aws/Dockerrun.aws.json @@ -1,7 +1,7 @@ {
"AWSEBDockerrunVersion": "1",
"Image": {
- "Name": "mattermost/platform:1.1",
+ "Name": "mattermost/platform:1.3",
"Update": "true"
},
"Ports": [
diff --git a/docker/1.3/README.md b/docker/1.3/README.md new file mode 100644 index 000000000..f737a1554 --- /dev/null +++ b/docker/1.3/README.md @@ -0,0 +1,23 @@ +Mattermost +========== + +http:/mattermost.org + +Mattermost is an open-source team communication service. It brings team messaging and file sharing into one place, accessible across PCs and phones, with archiving and search. + +Installing Mattermost +===================== + +To run an instance of the latest version of mattermost on your local machine you can run: + +`docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform` + +To update this image to the latest version you can run: + +`docker pull mattermost/platform` + +To run an instance of the latest code from the master branch on GitHub you can run: + +`docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform:dev` + +Any questions, please visit http://forum.mattermost.org diff --git a/docker/1.1/config_docker.json b/docker/1.3/config_docker.json index 653b6ffd7..a35abb9da 100644 --- a/docker/1.1/config_docker.json +++ b/docker/1.3/config_docker.json @@ -5,7 +5,8 @@ "SegmentDeveloperKey": "", "GoogleDeveloperKey": "", "EnableOAuthServiceProvider": false, - "EnableIncomingWebhooks": true, + "EnableIncomingWebhooks": false, + "EnableOutgoingWebhooks": false, "EnablePostUsernameOverride": false, "EnablePostIconOverride": false, "EnableTesting": false, @@ -16,7 +17,9 @@ "MaxUsersPerTeam": 50, "EnableTeamCreation": true, "EnableUserCreation": true, - "RestrictCreationToDomains": "" + "RestrictCreationToDomains": "", + "RestrictTeamNames": true, + "EnableTeamListing": false }, "SqlSettings": { "DriverName": "mysql", @@ -65,9 +68,8 @@ "ConnectionSecurity": "", "InviteSalt": "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9YoS", "PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5eL", - "ApplePushServer": "", - "ApplePushCertPublic": "", - "ApplePushCertPrivate": "" + "SendPushNotifications": false, + "PushNotificationServer": "" }, "RateLimitSettings": { "EnableRateLimiter": true, diff --git a/docker/1.1/docker-entry.sh b/docker/1.3/docker-entry.sh index 6bd2a1263..6bd2a1263 100755 --- a/docker/1.1/docker-entry.sh +++ b/docker/1.3/docker-entry.sh diff --git a/model/version.go b/model/version.go index af99717cd..5e41a28d1 100644 --- a/model/version.go +++ b/model/version.go @@ -12,6 +12,7 @@ import ( // It should be maitained in chronological order with most current // release at the front of the list. var versions = []string{ + "1.3.0", "1.2.1", "1.2.0", "1.1.0", diff --git a/store/sql_post_store.go b/store/sql_post_store.go index be770c09e..40dca9930 100644 --- a/store/sql_post_store.go +++ b/store/sql_post_store.go @@ -38,8 +38,6 @@ func NewSqlPostStore(sqlStore *SqlStore) PostStore { } func (s SqlPostStore) UpgradeSchemaIfNeeded() { - s.RemoveColumnIfExists("Posts", "ImgCount") // remove after 1.3 release - s.GetMaster().Exec(`UPDATE Preferences SET Type = :NewType WHERE Type = :CurrentType`, map[string]string{"NewType": model.POST_JOIN_LEAVE, "CurrentType": "join_leave"}) // remove after 1.3 release } func (s SqlPostStore) CreateIndexesIfNotExists() { diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx index 695d7daef..b7711ca29 100644 --- a/web/react/components/post.jsx +++ b/web/react/components/post.jsx @@ -179,7 +179,7 @@ export default class Post extends React.Component { } let profilePic = null; - if (!this.props.hideProfilePic) { + if (!this.props.hideProfilePic && post.user_id !== '0') { let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex(); if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') { if (post.props.override_icon_url) { diff --git a/web/react/stores/post_store.jsx b/web/react/stores/post_store.jsx index 2212edadb..8fedba9ce 100644 --- a/web/react/stores/post_store.jsx +++ b/web/react/stores/post_store.jsx @@ -7,6 +7,9 @@ import EventEmitter from 'events'; import ChannelStore from '../stores/channel_store.jsx'; import BrowserStore from '../stores/browser_store.jsx'; import UserStore from '../stores/user_store.jsx'; +import SocketStore from '../stores/socket_store.jsx'; + +import * as Utils from '../utils/utils.jsx'; import Constants from '../utils/constants.jsx'; const ActionTypes = Constants.ActionTypes; @@ -53,11 +56,14 @@ class PostStoreClass extends EventEmitter { this.storePost = this.storePost.bind(this); this.storeFocusedPost = this.storeFocusedPost.bind(this); this.checkBounds = this.checkBounds.bind(this); + this.addEphemeralPost = this.addEphemeralPost.bind(this); this.clearFocusedPost = this.clearFocusedPost.bind(this); this.clearChannelVisibility = this.clearChannelVisibility.bind(this); this.removePost = this.removePost.bind(this); + this.deletePost = this.deletePost.bind(this); + this.clearUnseenDeletedPosts = this.clearUnseenDeletedPosts.bind(this); this.getPendingPosts = this.getPendingPosts.bind(this); this.storePendingPost = this.storePendingPost.bind(this); @@ -65,10 +71,6 @@ class PostStoreClass extends EventEmitter { this.clearPendingPosts = this.clearPendingPosts.bind(this); this.updatePendingPost = this.updatePendingPost.bind(this); - this.storeUnseenDeletedPost = this.storeUnseenDeletedPost.bind(this); - this.getUnseenDeletedPosts = this.getUnseenDeletedPosts.bind(this); - this.clearUnseenDeletedPosts = this.clearUnseenDeletedPosts.bind(this); - // These functions are bad and work should be done to remove this system when the RHS dies this.storeSelectedPost = this.storeSelectedPost.bind(this); this.getSelectedPost = this.getSelectedPost.bind(this); @@ -211,28 +213,6 @@ class PostStoreClass extends EventEmitter { postList.order = this.postsInfo[id].pendingPosts.order.concat(postList.order); } - // Add deleted posts - if (this.postsInfo[id].hasOwnProperty('deletedPosts')) { - Object.assign(postList.posts, this.postsInfo[id].deletedPosts); - - for (const postID in this.postsInfo[id].deletedPosts) { - if (this.postsInfo[id].deletedPosts.hasOwnProperty(postID)) { - postList.order.push(postID); - } - } - - // Merge would be faster - postList.order.sort((a, b) => { - if (postList.posts[a].create_at > postList.posts[b].create_at) { - return -1; - } - if (postList.posts[a].create_at < postList.posts[b].create_at) { - return 1; - } - return 0; - }); - } - return postList; } @@ -439,33 +419,88 @@ class PostStoreClass extends EventEmitter { this.emitChange(); } - storeUnseenDeletedPost(post) { - let posts = this.getUnseenDeletedPosts(post.channel_id); + addEphemeralPost(post, disableNotification, autoDeleteTimer) { + if (!post.channel_id) { + return null; + } - if (!posts) { - posts = {}; + const member = ChannelStore.getMember(post.channel_id); + if (!member) { + return null; } - post.message = '(message deleted)'; - post.state = Constants.POST_DELETED; - post.filenames = []; + const timestamp = Utils.getTimestamp(); + const newPost = $.extend(true, { + id: Utils.generateId(), + user_id: '0', + create_at: timestamp, + update_at: timestamp, + filenames: [], + props: {} + }, post); + + if (disableNotification) { + newPost.props.disable_notification = true; + } + + SocketStore.handleMessage({ + user_id: newPost.user_id, + channel_id: newPost.channel_id, + action: Constants.SocketEvents.POSTED, + props: { + post: JSON.stringify(newPost), + ephemeral: true + } + }); + + if (autoDeleteTimer) { + setTimeout(() => { + ChannelStore.resetCounts(newPost.channel_id); + this.removePost(newPost); + this.emitChange(); + ChannelStore.emitChange(); + }, autoDeleteTimer); + } - posts[post.id] = post; - this.postsInfo[post.channel_id].deletedPosts = posts; + return newPost; } - getUnseenDeletedPosts(channelId) { - if (this.postsInfo.hasOwnProperty(channelId)) { - return this.postsInfo[channelId].deletedPosts; + deletePost(post) { + const newId = Utils.generateId(); + const index = this.postsInfo[post.channel_id].postList.order.indexOf(post.id); + this.postsInfo[post.channel_id].postList.order.splice(index, 1, newId); + this.removePost(post); + + post.id = newId; + post.message = '(message deleted)'; + post.state = Constants.POST_DELETED; + post.filenames = []; + if (post.props && post.props.attachments) { + Reflect.deleteProperty(post.props, 'attachments'); } - return null; + Utils.defer(() => { + this.addEphemeralPost(post, true); + }); } clearUnseenDeletedPosts(channelId) { - if (this.postsInfo.hasOwnProperty(channelId)) { - Reflect.deleteProperty(this.postsInfo[channelId], 'deletedPosts'); + const postList = this.postsInfo[channelId] && this.postsInfo[channelId].postList; + if (!postList) { + return; + } + + const member = ChannelStore.getMember(channelId); + if (!member) { + return; } + + Object.keys(postList.posts).forEach((postId) => { + const post = postList.posts[postId]; + if (post.state === Constants.POST_DELETED && post.create_at < member.last_viewed_at) { + this.removePost(post); + } + }); } storeSelectedPost(postList) { @@ -618,9 +653,7 @@ PostStore.dispatchToken = AppDispatcher.register((payload) => { PostStore.jumpPostsViewToBottom(); break; case ActionTypes.POST_DELETED: - PostStore.storeUnseenDeletedPost(action.post); - PostStore.removePost(action.post); - PostStore.emitChange(); + PostStore.deletePost(action.post); break; case ActionTypes.RECIEVED_POST_SELECTED: PostStore.storeSelectedPost(action.post_list); diff --git a/web/react/stores/socket_store.jsx b/web/react/stores/socket_store.jsx index d5aed40cf..87467b02f 100644 --- a/web/react/stores/socket_store.jsx +++ b/web/react/stores/socket_store.jsx @@ -160,11 +160,15 @@ function handleNewPostEvent(msg) { AsyncClient.updateLastViewedAt(); } } else if (UserStore.getCurrentId() !== msg.user_id || post.type !== Constants.POST_TYPE_JOIN_LEAVE) { - AsyncClient.getChannel(msg.channel_id); + if (msg.props.ephemeral) { + AsyncClient.getChannelAndAddUnreadMessages(msg.channel_id, 1); + } else { + AsyncClient.getChannel(msg.channel_id); + } } // Send desktop notification - if ((UserStore.getCurrentId() !== msg.user_id || post.props.from_webhook === 'true') && !Utils.isSystemMessage(post)) { + if ((UserStore.getCurrentId() !== msg.user_id || post.props.from_webhook === 'true') && !Utils.isSystemMessage(post) && !post.props.disable_notification) { const msgProps = msg.props; let mentions = []; diff --git a/web/react/utils/async_client.jsx b/web/react/utils/async_client.jsx index 88b5aa739..9cdbd73a4 100644 --- a/web/react/utils/async_client.jsx +++ b/web/react/utils/async_client.jsx @@ -82,7 +82,7 @@ export function getChannels(checkVersion) { ); } -export function getChannel(id) { +export function getChannelAndAddUnreadMessages(id, unreadCount) { if (isCallInProgress('getChannel' + id)) { return; } @@ -97,6 +97,7 @@ export function getChannel(id) { return; } + data.channel.total_msg_count += (unreadCount || 0); AppDispatcher.handleServerAction({ type: ActionTypes.RECIEVED_CHANNEL, channel: data.channel, @@ -110,6 +111,10 @@ export function getChannel(id) { ); } +export function getChannel(id) { + getChannelAndAddUnreadMessages(id, 0); +} + export function updateLastViewedAt(id) { let channelId; if (id) { @@ -131,6 +136,14 @@ export function updateLastViewedAt(id) { channelId, () => { callTracker.updateLastViewed = 0; + + var channel = ChannelStore.get(channelId); + var member = ChannelStore.getMember(channelId); + if (channel && member) { + member.msg_count = channel.total_msg_count; + member.last_viewed_at = utils.getTimestamp(); + ChannelStore.setChannelMember(member); + } }, (err) => { callTracker.updateLastViewed = 0; diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index c2e4276b0..13c7f961a 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -1254,3 +1254,8 @@ export function isFeatureEnabled(feature) { export function isSystemMessage(post) { return post.type && (post.type.lastIndexOf(Constants.SYSTEM_MESSAGE_PREFIX) === 0); } + +// convenience method to dispatch an event in JS' next event cycle +export function defer(fn) { + setTimeout(fn, 0); +} |