summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md29
-rw-r--r--api/admin.go1
-rw-r--r--api/channel.go2
-rw-r--r--api/context.go4
-rw-r--r--api/post.go1
-rw-r--r--api/team.go1
-rw-r--r--api/templates/email_change_body.html2
-rw-r--r--api/user.go7
-rw-r--r--docker/1.2/Dockerrun.aws.zipbin867 -> 0 bytes
-rwxr-xr-xdocker/1.2/Dockerrun.aws/Dockerrun.aws.json13
-rw-r--r--docker/1.2/config_docker.json95
-rw-r--r--docker/1.3/Dockerfile49
-rw-r--r--docker/1.3/Dockerrun.aws.zipbin867 -> 0 bytes
-rw-r--r--docker/1.3/Dockerrun.aws/.ebextensions/01_files.config14
-rw-r--r--docker/1.3/README.md23
-rwxr-xr-xdocker/1.3/docker-entry.sh111
-rw-r--r--docker/2.0/Dockerfile (renamed from docker/1.2/Dockerfile)2
-rw-r--r--docker/2.0/Dockerrun.aws.zipbin0 -> 1369 bytes
-rw-r--r--docker/2.0/Dockerrun.aws/.ebextensions/.zipbin0 -> 338 bytes
-rw-r--r--docker/2.0/Dockerrun.aws/.ebextensions/01_files.config (renamed from docker/1.2/Dockerrun.aws/.ebextensions/01_files.config)0
-rwxr-xr-xdocker/2.0/Dockerrun.aws/Dockerrun.aws.json (renamed from docker/1.3/Dockerrun.aws/Dockerrun.aws.json)2
-rw-r--r--docker/2.0/README.md (renamed from docker/1.2/README.md)0
-rw-r--r--docker/2.0/config_docker.json (renamed from docker/1.3/config_docker.json)26
-rwxr-xr-xdocker/2.0/docker-entry.sh (renamed from docker/1.2/docker-entry.sh)0
-rw-r--r--model/gitlab/gitlab.go27
-rw-r--r--model/version.go1
-rw-r--r--model/version_test.go15
-rw-r--r--store/sql_channel_store.go31
-rw-r--r--store/sql_channel_store_test.go12
-rw-r--r--web/react/components/admin_console/user_item.jsx2
-rw-r--r--web/react/components/channel_info_modal.jsx2
-rw-r--r--web/react/components/channel_loader.jsx4
-rw-r--r--web/react/components/navbar.jsx2
-rw-r--r--web/react/components/popover_list_members.jsx2
-rw-r--r--web/react/components/post_info.jsx3
-rw-r--r--web/react/components/posts_view.jsx10
-rw-r--r--web/react/components/rhs_comment.jsx22
-rw-r--r--web/react/components/rhs_root_post.jsx22
-rw-r--r--web/react/components/search_results.jsx15
-rw-r--r--web/react/components/team_general_tab.jsx8
-rw-r--r--web/react/components/team_signup_email_item.jsx2
-rw-r--r--web/react/components/team_signup_send_invites_page.jsx4
-rw-r--r--web/react/components/user_settings/manage_command_hooks.jsx4
-rw-r--r--web/react/components/user_settings/manage_incoming_hooks.jsx2
-rw-r--r--web/react/components/user_settings/manage_outgoing_hooks.jsx9
-rw-r--r--web/react/components/user_settings/user_settings_modal.jsx19
-rw-r--r--web/react/components/user_settings/user_settings_security.jsx8
-rw-r--r--web/react/package.json1
-rw-r--r--web/react/stores/channel_store.jsx4
-rw-r--r--web/react/stores/post_store.jsx17
-rw-r--r--web/react/stores/socket_store.jsx20
-rw-r--r--web/react/utils/async_client.jsx15
-rw-r--r--web/react/utils/markdown.jsx4
-rw-r--r--web/react/utils/utils.jsx4
-rw-r--r--web/sass-files/sass/partials/_markdown.scss9
-rw-r--r--web/sass-files/sass/partials/_modal.scss119
-rw-r--r--web/sass-files/sass/partials/_responsive.scss18
-rw-r--r--web/sass-files/sass/partials/_settings.scss1
-rw-r--r--web/sass-files/sass/partials/_tutorial.scss2
-rw-r--r--web/static/i18n/en.json24
-rw-r--r--web/static/i18n/es.json27
-rw-r--r--web/templates/head.html1
62 files changed, 396 insertions, 478 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c27041165..8606fc72c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -144,24 +144,37 @@ The following is for informational purposes only, no action needed. Mattermost a
##### Licenses Table
1. Added `Licenses` Table
+##### Commands Table
+1. Added `Commands` Table
+
#### Known Issues
- Navigating to a page with new messages containing inline images added via markdown causes the channel to scroll up and down while loading the inline images.
-- Microsoft Edge does not yet support drag and drop for file attachments.
+- Microsoft Edge does not yet support drag and drop for file attachments.
+- No error message on IE11 when uploading more than 5 files or a file over 50 MB.
+- File name tooltip stays open after clicking to download.
- Scroll bar does not appear in the center channel.
- Unable to paste images into the text box on Firefox, Safari, and IE11.
-- Importing from Slack fails to load messages in certain cases and breaks @mentions.
-- System Console > TEAMS > Statistics > Newly Created Users shows all users as created "just now".
-- Favicon does not turn red when @mentions and direct messages are received in an inactive browser tab.
+- Importing from Slack fails to load channels in certain cases.
+- System Console > Teams > Statistics > Newly Created Users shows all users as created "just now".
+- Username and email display on single line in System Console user management tab.
- Searching for a phrase in quotations returns more than just the phrase on installations with a Postgres database.
- Archived channels are not removed from the "More" menu for the person that archived the channel until after refresh.
+- First load of an empty channel does not display the introduction message.
- Search results don't highlight searches for @username, non-latin characters, or terms inside Markdown code blocks.
- Searching for a username or hashtag containing a dot returns a search where the dot is replaced with the "or" operator.
+- Search term highlighting doesn't update on IE11 when search terms change but return the same posts.
- Hashtags less than three characters long are not searchable.
-- Users remains in the channel counter after being deactivated.
-- Messages with symbols (<,>,-,+,=,%,^,#,*,|) directly before or after a hashtag are not searchable.
-- Permalinks for the second message or later consecutively sent in a group by the same author displaces the copy link popover or causes an error
-- Emoji smileys ending with a letter at the end of a message do not auto-complete as expected
+- Hashtags containing a dash incorrectly highlight in the search results.
+- Users remain in the channel counter after being deactivated.
+- Permalinks for the second message or later consecutively sent in a group by the same author displaces the copy link popover or causes an error.
+- Emoji smileys ending with a letter at the end of a message do not auto-complete as expected.
+- Logout slash command does not force a logout.
+- Incorrect formatting when a new line is added directly after a list.
+- Timestamps are displayed in 12-hour format when set to 24-hour format.
+- GIF links inside code blocks auto-post the GIFs.
+- Syntax highlighting code block is missing the label for Latex documents.
+- Deleted messages don't delete in the RHS until a page refresh.
#### Contributors
diff --git a/api/admin.go b/api/admin.go
index e8cb8b3c7..d04991353 100644
--- a/api/admin.go
+++ b/api/admin.go
@@ -120,6 +120,7 @@ func getConfig(c *Context, w http.ResponseWriter, r *http.Request) {
cfg := model.ConfigFromJson(strings.NewReader(json))
json = cfg.ToJson()
+ w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
w.Write([]byte(json))
}
diff --git a/api/channel.go b/api/channel.go
index ff5b0f8da..e97e08fc0 100644
--- a/api/channel.go
+++ b/api/channel.go
@@ -729,7 +729,6 @@ func getChannel(c *Context, w http.ResponseWriter, r *http.Request) {
return
} else {
w.Header().Set(model.HEADER_ETAG_SERVER, data.Etag())
- w.Header().Set("Expires", "-1")
w.Write([]byte(data.ToJson()))
}
}
@@ -798,7 +797,6 @@ func getChannelExtraInfo(c *Context, w http.ResponseWriter, r *http.Request) {
data := model.ChannelExtra{Id: channel.Id, Members: extraMembers, MemberCount: memberCount}
w.Header().Set(model.HEADER_ETAG_SERVER, extraEtag)
- w.Header().Set("Expires", "-1")
w.Write([]byte(data.ToJson()))
}
}
diff --git a/api/context.go b/api/context.go
index b91981ecd..d0b4f85d2 100644
--- a/api/context.go
+++ b/api/context.go
@@ -165,6 +165,10 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} else {
// All api response bodies will be JSON formatted by default
w.Header().Set("Content-Type", "application/json")
+
+ if r.Method == "GET" {
+ w.Header().Set("Expires", "0")
+ }
}
if len(token) != 0 {
diff --git a/api/post.go b/api/post.go
index fadabd66e..9d3ba5ab1 100644
--- a/api/post.go
+++ b/api/post.go
@@ -1197,6 +1197,5 @@ func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) {
}
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
- w.Header().Set("Expires", "0")
w.Write([]byte(posts.ToJson()))
}
diff --git a/api/team.go b/api/team.go
index 6d59e94e9..052d6e698 100644
--- a/api/team.go
+++ b/api/team.go
@@ -647,7 +647,6 @@ func getMyTeam(c *Context, w http.ResponseWriter, r *http.Request) {
return
} else {
w.Header().Set(model.HEADER_ETAG_SERVER, result.Data.(*model.Team).Etag())
- w.Header().Set("Expires", "-1")
w.Write([]byte(result.Data.(*model.Team).ToJson()))
return
}
diff --git a/api/templates/email_change_body.html b/api/templates/email_change_body.html
index 4f28584c4..41b1bcd7d 100644
--- a/api/templates/email_change_body.html
+++ b/api/templates/email_change_body.html
@@ -18,7 +18,7 @@
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">{{.Props.Title}}</h2>
- <p>{{.Props.Info}}</p>
+ <p>{{.Html.Info}}</p>
</td>
</tr>
<tr>
diff --git a/api/user.go b/api/user.go
index 8f381aeda..7919da168 100644
--- a/api/user.go
+++ b/api/user.go
@@ -897,7 +897,6 @@ func getMe(c *Context, w http.ResponseWriter, r *http.Request) {
} else {
result.Data.(*model.User).Sanitize(map[string]bool{})
w.Header().Set(model.HEADER_ETAG_SERVER, result.Data.(*model.User).Etag())
- w.Header().Set("Expires", "-1")
w.Write([]byte(result.Data.(*model.User).ToJson()))
return
}
@@ -1761,14 +1760,14 @@ func sendEmailChangeEmailAndForget(c *Context, oldEmail, newEmail, teamDisplayNa
go func() {
subjectPage := NewServerTemplatePage("email_change_subject", c.Locale)
- subjectPage.Props["Subject"] = c.T("api.templates.email_change_body",
+ subjectPage.Props["Subject"] = c.T("api.templates.email_change_subject",
map[string]interface{}{"TeamDisplayName": teamDisplayName})
bodyPage := NewServerTemplatePage("email_change_body", c.Locale)
bodyPage.Props["SiteURL"] = siteURL
bodyPage.Props["Title"] = c.T("api.templates.email_change_body.title")
- bodyPage.Props["Info"] = c.T("api.templates.email_change_body.info",
- map[string]interface{}{"TeamDisplayName": teamDisplayName, "NewEmail": newEmail})
+ bodyPage.Html["Info"] = template.HTML(c.T("api.templates.email_change_body.info",
+ map[string]interface{}{"TeamDisplayName": teamDisplayName, "NewEmail": newEmail}))
if err := utils.SendMail(oldEmail, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error(utils.T("api.user.send_email_change_email_and_forget.error"), err)
diff --git a/docker/1.2/Dockerrun.aws.zip b/docker/1.2/Dockerrun.aws.zip
deleted file mode 100644
index 4de6ec362..000000000
--- a/docker/1.2/Dockerrun.aws.zip
+++ /dev/null
Binary files differ
diff --git a/docker/1.2/Dockerrun.aws/Dockerrun.aws.json b/docker/1.2/Dockerrun.aws/Dockerrun.aws.json
deleted file mode 100755
index c32a998e4..000000000
--- a/docker/1.2/Dockerrun.aws/Dockerrun.aws.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "AWSEBDockerrunVersion": "1",
- "Image": {
- "Name": "mattermost/platform:1.2",
- "Update": "true"
- },
- "Ports": [
- {
- "ContainerPort": "80"
- }
- ],
- "Logging": "/var/log/"
-}
diff --git a/docker/1.2/config_docker.json b/docker/1.2/config_docker.json
deleted file mode 100644
index c23a72cd1..000000000
--- a/docker/1.2/config_docker.json
+++ /dev/null
@@ -1,95 +0,0 @@
-{
- "ServiceSettings": {
- "ListenAddress": ":80",
- "MaximumLoginAttempts": 10,
- "SegmentDeveloperKey": "",
- "GoogleDeveloperKey": "",
- "EnableOAuthServiceProvider": false,
- "EnableIncomingWebhooks": false,
- "EnableOutgoingWebhooks": false,
- "EnablePostUsernameOverride": false,
- "EnablePostIconOverride": false,
- "EnableTesting": false,
- "EnableSecurityFixAlert": true
- },
- "TeamSettings": {
- "SiteName": "Mattermost",
- "MaxUsersPerTeam": 50,
- "EnableTeamCreation": true,
- "EnableUserCreation": true,
- "RestrictCreationToDomains": "",
- "RestrictTeamNames": true,
- "EnableTeamListing": false
- },
- "SqlSettings": {
- "DriverName": "mysql",
- "DataSource": "mmuser:mostest@tcp(dockerhost:3306)/mattermost_test?charset=utf8mb4,utf8",
- "DataSourceReplicas": [],
- "MaxIdleConns": 10,
- "MaxOpenConns": 10,
- "Trace": false,
- "AtRestEncryptKey": "7rAh6iwQCkV4cA1Gsg3fgGOXJAQ43QVg"
- },
- "LogSettings": {
- "EnableConsole": false,
- "ConsoleLevel": "INFO",
- "EnableFile": true,
- "FileLevel": "INFO",
- "FileFormat": "",
- "FileLocation": ""
- },
- "FileSettings": {
- "DriverName": "local",
- "Directory": "/mattermost/data/",
- "EnablePublicLink": true,
- "PublicLinkSalt": "A705AklYF8MFDOfcwh3I488G8vtLlVip",
- "ThumbnailWidth": 120,
- "ThumbnailHeight": 100,
- "PreviewWidth": 1024,
- "PreviewHeight": 0,
- "ProfileWidth": 128,
- "ProfileHeight": 128,
- "InitialFont": "luximbi.ttf",
- "AmazonS3AccessKeyId": "",
- "AmazonS3SecretAccessKey": "",
- "AmazonS3Bucket": "",
- "AmazonS3Region": ""
- },
- "EmailSettings": {
- "EnableSignUpWithEmail": true,
- "SendEmailNotifications": false,
- "RequireEmailVerification": false,
- "FeedbackName": "",
- "FeedbackEmail": "",
- "SMTPUsername": "",
- "SMTPPassword": "",
- "SMTPServer": "",
- "SMTPPort": "",
- "ConnectionSecurity": "",
- "InviteSalt": "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9YoS",
- "PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5eL",
- "ApplePushServer": "",
- "ApplePushCertPublic": "",
- "ApplePushCertPrivate": ""
- },
- "RateLimitSettings": {
- "EnableRateLimiter": true,
- "PerSec": 10,
- "MemoryStoreSize": 10000,
- "VaryByRemoteAddr": true,
- "VaryByHeader": ""
- },
- "PrivacySettings": {
- "ShowEmailAddress": true,
- "ShowFullName": true
- },
- "GitLabSettings": {
- "Enable": false,
- "Secret": "",
- "Id": "",
- "Scope": "",
- "AuthEndpoint": "",
- "TokenEndpoint": "",
- "UserApiEndpoint": ""
- }
-}
diff --git a/docker/1.3/Dockerfile b/docker/1.3/Dockerfile
deleted file mode 100644
index 4a25198af..000000000
--- a/docker/1.3/Dockerfile
+++ /dev/null
@@ -1,49 +0,0 @@
-# Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-# See License.txt for license information.
-FROM ubuntu:14.04
-
-#
-# Install SQL
-#
-
-ENV MYSQL_ROOT_PASSWORD=mostest
-ENV MYSQL_USER=mmuser
-ENV MYSQL_PASSWORD=mostest
-ENV MYSQL_DATABASE=mattermost_test
-
-RUN groupadd -r mysql && useradd -r -g mysql mysql
-
-RUN apt-key adv --keyserver pool.sks-keyservers.net --recv-keys A4A9406876FCBD3C456770C88C718D3B5072E1F5
-
-ENV MYSQL_MAJOR 5.6
-ENV MYSQL_VERSION 5.6.25
-
-RUN echo "deb http://repo.mysql.com/apt/debian/ wheezy mysql-${MYSQL_MAJOR}" > /etc/apt/sources.list.d/mysql.list
-
-RUN apt-get update \
- && export DEBIAN_FRONTEND=noninteractive \
- && apt-get -y install perl wget mysql-server \
- && rm -rf /var/lib/apt/lists/* \
- && rm -rf /var/lib/mysql && mkdir -p /var/lib/mysql
-
-RUN sed -Ei 's/^(bind-address|log)/#&/' /etc/mysql/my.cnf
-
-VOLUME /var/lib/mysql
-# ---------------------------------------------------------------------------------------------------------------------
-
-WORKDIR /mattermost
-
-# Copy over files
-ADD https://github.com/mattermost/platform/releases/download/v1.3.0/mattermost.tar.gz /
-RUN tar -zxvf /mattermost.tar.gz --strip-components=1 && rm /mattermost.tar.gz
-ADD config_docker.json /
-ADD docker-entry.sh /
-
-RUN chmod +x /docker-entry.sh
-ENTRYPOINT /docker-entry.sh
-
-# Create default storage directory
-RUN mkdir /mattermost-data/
-
-# Ports
-EXPOSE 80
diff --git a/docker/1.3/Dockerrun.aws.zip b/docker/1.3/Dockerrun.aws.zip
deleted file mode 100644
index dd201d990..000000000
--- a/docker/1.3/Dockerrun.aws.zip
+++ /dev/null
Binary files differ
diff --git a/docker/1.3/Dockerrun.aws/.ebextensions/01_files.config b/docker/1.3/Dockerrun.aws/.ebextensions/01_files.config
deleted file mode 100644
index 7f40a8b34..000000000
--- a/docker/1.3/Dockerrun.aws/.ebextensions/01_files.config
+++ /dev/null
@@ -1,14 +0,0 @@
-files:
- "/etc/nginx/conf.d/proxy.conf":
- mode: "000755"
- owner: root
- group: root
- content: |
- client_max_body_size 50M;
- "/opt/elasticbeanstalk/hooks/appdeploy/post/init.sh":
- mode: "000755"
- owner: root
- group: root
- content: |
- #!/usr/bin/env bash
- gpasswd -a ec2-user docker
diff --git a/docker/1.3/README.md b/docker/1.3/README.md
deleted file mode 100644
index f737a1554..000000000
--- a/docker/1.3/README.md
+++ /dev/null
@@ -1,23 +0,0 @@
-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.3/docker-entry.sh b/docker/1.3/docker-entry.sh
deleted file mode 100755
index 6bd2a1263..000000000
--- a/docker/1.3/docker-entry.sh
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/bin/bash
-# Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-# See License.txt for license information.
-
-mkdir -p web/static/js
-
-echo "127.0.0.1 dockerhost" >> /etc/hosts
-/etc/init.d/networking restart
-
-echo configuring mysql
-
-# SQL!!!
-set -e
-
-get_option () {
- local section=$1
- local option=$2
- local default=$3
- ret=$(my_print_defaults $section | grep '^--'${option}'=' | cut -d= -f2-)
- [ -z $ret ] && ret=$default
- echo $ret
-}
-
-
-# Get config
-DATADIR="$("mysqld" --verbose --help 2>/dev/null | awk '$1 == "datadir" { print $2; exit }')"
-SOCKET=$(get_option mysqld socket "$DATADIR/mysql.sock")
-PIDFILE=$(get_option mysqld pid-file "/var/run/mysqld/mysqld.pid")
-
-if [ ! -d "$DATADIR/mysql" ]; then
- if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" ]; then
- echo >&2 'error: database is uninitialized and MYSQL_ROOT_PASSWORD not set'
- echo >&2 ' Did you forget to add -e MYSQL_ROOT_PASSWORD=... ?'
- exit 1
- fi
-
- mkdir -p "$DATADIR"
- chown -R mysql:mysql "$DATADIR"
-
- echo 'Running mysql_install_db'
- mysql_install_db --user=mysql --datadir="$DATADIR" --rpm --keep-my-cnf
- echo 'Finished mysql_install_db'
-
- mysqld --user=mysql --datadir="$DATADIR" --skip-networking &
- for i in $(seq 30 -1 0); do
- [ -S "$SOCKET" ] && break
- echo 'MySQL init process in progress...'
- sleep 1
- done
- if [ $i = 0 ]; then
- echo >&2 'MySQL init process failed.'
- exit 1
- fi
-
- # These statements _must_ be on individual lines, and _must_ end with
- # semicolons (no line breaks or comments are permitted).
- # TODO proper SQL escaping on ALL the things D:
-
- tempSqlFile=$(mktemp /tmp/mysql-first-time.XXXXXX.sql)
- cat > "$tempSqlFile" <<-EOSQL
- -- What's done in this file shouldn't be replicated
- -- or products like mysql-fabric won't work
- SET @@SESSION.SQL_LOG_BIN=0;
-
- DELETE FROM mysql.user ;
- CREATE USER 'root'@'%' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ;
- GRANT ALL ON *.* TO 'root'@'%' WITH GRANT OPTION ;
- DROP DATABASE IF EXISTS test ;
- EOSQL
-
- if [ "$MYSQL_DATABASE" ]; then
- echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" >> "$tempSqlFile"
- fi
-
- if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then
- echo "CREATE USER '"$MYSQL_USER"'@'%' IDENTIFIED BY '"$MYSQL_PASSWORD"' ;" >> "$tempSqlFile"
-
- if [ "$MYSQL_DATABASE" ]; then
- echo "GRANT ALL ON \`"$MYSQL_DATABASE"\`.* TO '"$MYSQL_USER"'@'%' ;" >> "$tempSqlFile"
- fi
- fi
-
- echo 'FLUSH PRIVILEGES ;' >> "$tempSqlFile"
-
- mysql -uroot < "$tempSqlFile"
-
- rm -f "$tempSqlFile"
- kill $(cat $PIDFILE)
- for i in $(seq 30 -1 0); do
- [ -f "$PIDFILE" ] || break
- echo 'MySQL init process in progress...'
- sleep 1
- done
- if [ $i = 0 ]; then
- echo >&2 'MySQL hangs during init process.'
- exit 1
- fi
- echo 'MySQL init process done. Ready for start up.'
-fi
-
-chown -R mysql:mysql "$DATADIR"
-
-mysqld &
-
-sleep 5
-
-# ------------------------
-
-echo starting platform
-cd /mattermost/bin
-./platform -config=/config_docker.json
diff --git a/docker/1.2/Dockerfile b/docker/2.0/Dockerfile
index e00c4e5ca..0f7a13e45 100644
--- a/docker/1.2/Dockerfile
+++ b/docker/2.0/Dockerfile
@@ -34,7 +34,7 @@ VOLUME /var/lib/mysql
WORKDIR /mattermost
# Copy over files
-ADD https://github.com/mattermost/platform/releases/download/v1.2.1/mattermost.tar.gz /
+ADD https://github.com/mattermost/platform/releases/download/v2.0.0-rc2/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/2.0/Dockerrun.aws.zip b/docker/2.0/Dockerrun.aws.zip
new file mode 100644
index 000000000..bfbe56400
--- /dev/null
+++ b/docker/2.0/Dockerrun.aws.zip
Binary files differ
diff --git a/docker/2.0/Dockerrun.aws/.ebextensions/.zip b/docker/2.0/Dockerrun.aws/.ebextensions/.zip
new file mode 100644
index 000000000..053a7f725
--- /dev/null
+++ b/docker/2.0/Dockerrun.aws/.ebextensions/.zip
Binary files differ
diff --git a/docker/1.2/Dockerrun.aws/.ebextensions/01_files.config b/docker/2.0/Dockerrun.aws/.ebextensions/01_files.config
index 7f40a8b34..7f40a8b34 100644
--- a/docker/1.2/Dockerrun.aws/.ebextensions/01_files.config
+++ b/docker/2.0/Dockerrun.aws/.ebextensions/01_files.config
diff --git a/docker/1.3/Dockerrun.aws/Dockerrun.aws.json b/docker/2.0/Dockerrun.aws/Dockerrun.aws.json
index d4027e67c..7c739c0c5 100755
--- a/docker/1.3/Dockerrun.aws/Dockerrun.aws.json
+++ b/docker/2.0/Dockerrun.aws/Dockerrun.aws.json
@@ -1,7 +1,7 @@
{
"AWSEBDockerrunVersion": "1",
"Image": {
- "Name": "mattermost/platform:1.3",
+ "Name": "mattermost/platform:2.0",
"Update": "true"
},
"Ports": [
diff --git a/docker/1.2/README.md b/docker/2.0/README.md
index f737a1554..f737a1554 100644
--- a/docker/1.2/README.md
+++ b/docker/2.0/README.md
diff --git a/docker/1.3/config_docker.json b/docker/2.0/config_docker.json
index a35abb9da..6a1290189 100644
--- a/docker/1.3/config_docker.json
+++ b/docker/2.0/config_docker.json
@@ -7,10 +7,18 @@
"EnableOAuthServiceProvider": false,
"EnableIncomingWebhooks": false,
"EnableOutgoingWebhooks": false,
+ "EnableCommands": false,
+ "EnableOnlyAdminIntegrations": true,
"EnablePostUsernameOverride": false,
"EnablePostIconOverride": false,
"EnableTesting": false,
- "EnableSecurityFixAlert": true
+ "EnableDeveloper": false,
+ "EnableSecurityFixAlert": true,
+ "EnableInsecureOutgoingConnections": false,
+ "SessionLengthWebInDays" : 30,
+ "SessionLengthMobileInDays" : 30,
+ "SessionLengthSSOInDays" : 30,
+ "SessionCacheInMinutes" : 10
},
"TeamSettings": {
"SiteName": "Mattermost",
@@ -53,10 +61,16 @@
"AmazonS3AccessKeyId": "",
"AmazonS3SecretAccessKey": "",
"AmazonS3Bucket": "",
- "AmazonS3Region": ""
+ "AmazonS3Region": "",
+ "AmazonS3Endpoint": "",
+ "AmazonS3BucketEndpoint": "",
+ "AmazonS3LocationConstraint": false,
+ "AmazonS3LowercaseBucket": false
},
"EmailSettings": {
"EnableSignUpWithEmail": true,
+ "EnableSignInWithEmail": true,
+ "EnableSignInWithUsername": false,
"SendEmailNotifications": false,
"RequireEmailVerification": false,
"FeedbackName": "",
@@ -82,6 +96,14 @@
"ShowEmailAddress": true,
"ShowFullName": true
},
+ "SupportSettings": {
+ "TermsOfServiceLink": "/static/help/terms.html",
+ "PrivacyPolicyLink": "/static/help/privacy.html",
+ "AboutLink": "/static/help/about.html",
+ "HelpLink": "/static/help/help.html",
+ "ReportAProblemLink": "/static/help/report_problem.html",
+ "SupportEmail": "feedback@mattermost.com"
+ },
"GitLabSettings": {
"Enable": false,
"Secret": "",
diff --git a/docker/1.2/docker-entry.sh b/docker/2.0/docker-entry.sh
index 6bd2a1263..6bd2a1263 100755
--- a/docker/1.2/docker-entry.sh
+++ b/docker/2.0/docker-entry.sh
diff --git a/model/gitlab/gitlab.go b/model/gitlab/gitlab.go
index 8b96c64f6..3ca499976 100644
--- a/model/gitlab/gitlab.go
+++ b/model/gitlab/gitlab.go
@@ -67,6 +67,18 @@ func gitLabUserFromJson(data io.Reader) *GitLabUser {
}
}
+func (glu *GitLabUser) IsValid() bool {
+ if glu.Id == 0 {
+ return false
+ }
+
+ if len(glu.Email) == 0 {
+ return false
+ }
+
+ return true
+}
+
func (glu *GitLabUser) getAuthData() string {
return strconv.FormatInt(glu.Id, 10)
}
@@ -76,9 +88,20 @@ func (m *GitLabProvider) GetIdentifier() string {
}
func (m *GitLabProvider) GetUserFromJson(data io.Reader) *model.User {
- return userFromGitLabUser(gitLabUserFromJson(data))
+ glu := gitLabUserFromJson(data)
+ if glu.IsValid() {
+ return userFromGitLabUser(glu)
+ }
+
+ return &model.User{}
}
func (m *GitLabProvider) GetAuthDataFromJson(data io.Reader) string {
- return gitLabUserFromJson(data).getAuthData()
+ glu := gitLabUserFromJson(data)
+
+ if glu.IsValid() {
+ return glu.getAuthData()
+ }
+
+ return ""
}
diff --git a/model/version.go b/model/version.go
index 69529e7a1..8fbd65d03 100644
--- a/model/version.go
+++ b/model/version.go
@@ -13,6 +13,7 @@ import (
// It should be maitained in chronological order with most current
// release at the front of the list.
var versions = []string{
+ "2.0.0",
"1.4.0",
"1.3.0",
"1.2.1",
diff --git a/model/version_test.go b/model/version_test.go
index d73273ce5..e0346c43a 100644
--- a/model/version_test.go
+++ b/model/version_test.go
@@ -83,28 +83,23 @@ func TestIsCurrentVersion(t *testing.T) {
func TestIsPreviousVersionsSupported(t *testing.T) {
- // 1.4.0 CURRENT RELEASED VERSION
- if !IsPreviousVersionsSupported(versions[0]) {
+ if !IsPreviousVersionsSupported(versionsWithoutHotFixes[0]) {
t.Fatal()
}
- // 1.3.0
- if !IsPreviousVersionsSupported(versions[1]) {
+ if !IsPreviousVersionsSupported(versionsWithoutHotFixes[1]) {
t.Fatal()
}
- // 1.2.1
- if !IsPreviousVersionsSupported(versions[2]) {
+ if !IsPreviousVersionsSupported(versionsWithoutHotFixes[2]) {
t.Fatal()
}
- // 1.2.0
- if !IsPreviousVersionsSupported(versions[3]) {
+ if IsPreviousVersionsSupported(versionsWithoutHotFixes[4]) {
t.Fatal()
}
- // 1.1.0 NOT SUPPORTED
- if IsPreviousVersionsSupported(versions[4]) {
+ if IsPreviousVersionsSupported(versionsWithoutHotFixes[5]) {
t.Fatal()
}
}
diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go
index 8b52dae12..87ee2bb11 100644
--- a/store/sql_channel_store.go
+++ b/store/sql_channel_store.go
@@ -615,9 +615,36 @@ func (s SqlChannelStore) GetExtraMembers(channelId string, limit int) StoreChann
var err error
if limit != -1 {
- _, err = s.GetReplica().Select(&members, "SELECT Id, Nickname, Email, ChannelMembers.Roles, Username FROM ChannelMembers, Users WHERE ChannelMembers.UserId = Users.Id AND ChannelId = :ChannelId LIMIT :Limit", map[string]interface{}{"ChannelId": channelId, "Limit": limit})
+ _, err = s.GetReplica().Select(&members, `
+ SELECT
+ Id,
+ Nickname,
+ Email,
+ ChannelMembers.Roles,
+ Username
+ FROM
+ ChannelMembers,
+ Users
+ WHERE
+ ChannelMembers.UserId = Users.Id
+ AND Users.DeleteAt = 0
+ AND ChannelId = :ChannelId
+ LIMIT :Limit`, map[string]interface{}{"ChannelId": channelId, "Limit": limit})
} else {
- _, err = s.GetReplica().Select(&members, "SELECT Id, Nickname, Email, ChannelMembers.Roles, Username FROM ChannelMembers, Users WHERE ChannelMembers.UserId = Users.Id AND ChannelId = :ChannelId", map[string]interface{}{"ChannelId": channelId})
+ _, err = s.GetReplica().Select(&members, `
+ SELECT
+ Id,
+ Nickname,
+ Email,
+ ChannelMembers.Roles,
+ Username
+ FROM
+ ChannelMembers,
+ Users
+ WHERE
+ ChannelMembers.UserId = Users.Id
+ AND Users.DeleteAt = 0
+ AND ChannelId = :ChannelId`, map[string]interface{}{"ChannelId": channelId})
}
if err != nil {
diff --git a/store/sql_channel_store_test.go b/store/sql_channel_store_test.go
index a3b0c2286..816a85aef 100644
--- a/store/sql_channel_store_test.go
+++ b/store/sql_channel_store_test.go
@@ -377,6 +377,18 @@ func TestChannelMemberStore(t *testing.T) {
if t4 != t3 {
t.Fatal("Should not update time upon failure")
}
+
+ // rejoin the channel and make sure that an inactive user isn't returned by GetExtraMambers
+ Must(store.Channel().SaveMember(&o2))
+
+ u2.DeleteAt = 1000
+ Must(store.User().Update(&u2, true))
+
+ if result := <-store.Channel().GetExtraMembers(o1.ChannelId, 20); result.Err != nil {
+ t.Fatal(result.Err)
+ } else if extraMembers := result.Data.([]model.ExtraMember); len(extraMembers) != 1 {
+ t.Fatal("should have 1 extra members")
+ }
}
func TestChannelDeleteMemberStore(t *testing.T) {
diff --git a/web/react/components/admin_console/user_item.jsx b/web/react/components/admin_console/user_item.jsx
index 0c1a55cc1..009a9f004 100644
--- a/web/react/components/admin_console/user_item.jsx
+++ b/web/react/components/admin_console/user_item.jsx
@@ -353,7 +353,7 @@ export default class UserItem extends React.Component {
return (
<tr>
- <td className='row member-div'>
+ <td className='row member-div padding--equal'>
<img
className='post-profile-img pull-left'
src={`/api/v1/users/${user.id}/image?time=${user.update_at}&${Utils.getSessionIndex()}`}
diff --git a/web/react/components/channel_info_modal.jsx b/web/react/components/channel_info_modal.jsx
index 5067f5913..83f5aba65 100644
--- a/web/react/components/channel_info_modal.jsx
+++ b/web/react/components/channel_info_modal.jsx
@@ -56,7 +56,7 @@ class ChannelInfoModal extends React.Component {
</div>
<div className='col-sm-9'>{channelURL}</div>
</div>
- <div className='row'>
+ <div className='row form-group'>
<div className='col-sm-3 info__label'>
<FormattedMessage
id='channel_info.id'
diff --git a/web/react/components/channel_loader.jsx b/web/react/components/channel_loader.jsx
index 174c8c4e1..f3000ee05 100644
--- a/web/react/components/channel_loader.jsx
+++ b/web/react/components/channel_loader.jsx
@@ -95,6 +95,8 @@ class ChannelLoader extends React.Component {
$(window).on('focus', function windowFocus() {
AsyncClient.updateLastViewedAt();
+ ChannelStore.resetCounts(ChannelStore.getCurrentId());
+ ChannelStore.emitChange();
window.isActive = true;
});
@@ -185,4 +187,4 @@ ChannelLoader.propTypes = {
intl: intlShape.isRequired
};
-export default injectIntl(ChannelLoader); \ No newline at end of file
+export default injectIntl(ChannelLoader);
diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx
index e6a9fbd25..835298635 100644
--- a/web/react/components/navbar.jsx
+++ b/web/react/components/navbar.jsx
@@ -25,6 +25,7 @@ const ActionTypes = Constants.ActionTypes;
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import {FormattedMessage} from 'mm-intl';
+import attachFastClick from 'fastclick';
const Popover = ReactBootstrap.Popover;
const OverlayTrigger = ReactBootstrap.OverlayTrigger;
@@ -59,6 +60,7 @@ export default class Navbar extends React.Component {
ChannelStore.addChangeListener(this.onChange);
ChannelStore.addExtraInfoChangeListener(this.onChange);
$('.inner__wrap').click(this.hideSidebars);
+ attachFastClick(document.body);
}
componentWillUnmount() {
ChannelStore.removeChangeListener(this.onChange);
diff --git a/web/react/components/popover_list_members.jsx b/web/react/components/popover_list_members.jsx
index f217229ed..afff78bae 100644
--- a/web/react/components/popover_list_members.jsx
+++ b/web/react/components/popover_list_members.jsx
@@ -107,7 +107,7 @@ export default class PopoverListMembers extends React.Component {
name = Utils.displayUsername(teamMembers[m.username].id);
}
- if (name && teamMembers[m.username].delete_at <= 0) {
+ if (name) {
popoverHtml.push(
<div
className='text-nowrap'
diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx
index 6d82423d5..c44223b1f 100644
--- a/web/react/components/post_info.jsx
+++ b/web/react/components/post_info.jsx
@@ -144,7 +144,8 @@ export default class PostInfo extends React.Component {
);
}
- handlePermalink() {
+ handlePermalink(e) {
+ e.preventDefault();
EventHelpers.showGetPostLinkModal(this.props.post);
}
diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx
index ebe19abad..19ab7d5aa 100644
--- a/web/react/components/posts_view.jsx
+++ b/web/react/components/posts_view.jsx
@@ -535,7 +535,15 @@ function FloatingTimestamp({isScrolling, post}) {
return <noscript />;
}
- const dateString = Utils.getDateForUnixTicks(post.create_at).toDateString();
+ const dateString = (
+ <FormattedDate
+ value={post.create_at}
+ weekday='short'
+ day='2-digit'
+ month='short'
+ year='numeric'
+ />
+ );
let className = 'post-list__timestamp';
if (isScrolling) {
diff --git a/web/react/components/rhs_comment.jsx b/web/react/components/rhs_comment.jsx
index 9c85e9940..0d15c8599 100644
--- a/web/react/components/rhs_comment.jsx
+++ b/web/react/components/rhs_comment.jsx
@@ -31,6 +31,7 @@ class RhsComment extends React.Component {
this.retryComment = this.retryComment.bind(this);
this.parseEmojis = this.parseEmojis.bind(this);
+ this.handlePermalink = this.handlePermalink.bind(this);
this.state = {};
}
@@ -67,6 +68,10 @@ class RhsComment extends React.Component {
parseEmojis() {
twemoji.parse(ReactDOM.findDOMNode(this), {size: Constants.EMOJI_SIZE});
}
+ handlePermalink(e) {
+ e.preventDefault();
+ EventHelpers.showGetPostLinkModal(this.props.post);
+ }
componentDidMount() {
this.parseEmojis();
}
@@ -92,6 +97,23 @@ class RhsComment extends React.Component {
var dropdownContents = [];
+ dropdownContents.push(
+ <li
+ key='rhs-root-permalink'
+ role='presentation'
+ >
+ <a
+ href='#'
+ onClick={this.handlePermalink}
+ >
+ <FormattedMessage
+ id='rhs_comment.permalink'
+ defaultMessage='Permalink'
+ />
+ </a>
+ </li>
+ );
+
if (isOwner) {
dropdownContents.push(
<li
diff --git a/web/react/components/rhs_root_post.jsx b/web/react/components/rhs_root_post.jsx
index f9f7f8f81..54f2e8262 100644
--- a/web/react/components/rhs_root_post.jsx
+++ b/web/react/components/rhs_root_post.jsx
@@ -21,6 +21,7 @@ export default class RhsRootPost extends React.Component {
super(props);
this.parseEmojis = this.parseEmojis.bind(this);
+ this.handlePermalink = this.handlePermalink.bind(this);
this.state = {};
}
@@ -31,6 +32,10 @@ export default class RhsRootPost extends React.Component {
folder: Emoji.getImagePathForEmoticon()
});
}
+ handlePermalink(e) {
+ e.preventDefault();
+ EventHelpers.showGetPostLinkModal(this.props.post);
+ }
componentDidMount() {
this.parseEmojis();
}
@@ -83,6 +88,23 @@ export default class RhsRootPost extends React.Component {
var dropdownContents = [];
+ dropdownContents.push(
+ <li
+ key='rhs-root-permalink'
+ role='presentation'
+ >
+ <a
+ href='#'
+ onClick={this.handlePermalink}
+ >
+ <FormattedMessage
+ id='rhs_root.permalink'
+ defaultMessage='Permalink'
+ />
+ </a>
+ </li>
+ );
+
if (isOwner) {
dropdownContents.push(
<li
diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx
index 4adc3afe0..12c066734 100644
--- a/web/react/components/search_results.jsx
+++ b/web/react/components/search_results.jsx
@@ -15,13 +15,16 @@ function getStateFromStores() {
const results = SearchStore.getSearchResults();
const channels = new Map();
- const channelIds = results.order.map((postId) => results.posts[postId].channel_id);
- for (const id of channelIds) {
- if (channels.has(id)) {
- continue;
- }
- channels.set(id, ChannelStore.get(id));
+ if (results && results.order) {
+ const channelIds = results.order.map((postId) => results.posts[postId].channel_id);
+ for (const id of channelIds) {
+ if (channels.has(id)) {
+ continue;
+ }
+
+ channels.set(id, ChannelStore.get(id));
+ }
}
return {
diff --git a/web/react/components/team_general_tab.jsx b/web/react/components/team_general_tab.jsx
index 0a1b02853..c1b2a2e7f 100644
--- a/web/react/components/team_general_tab.jsx
+++ b/web/react/components/team_general_tab.jsx
@@ -486,13 +486,9 @@ class GeneralTab extends React.Component {
inputs.push(
<div key='teamInviteSetting'>
<div className='row'>
- <label className='col-sm-5 control-label'>
- <FormattedMessage
- id='general_tab.codeTitle'
- defaultMessage='Invite Code'
- />
+ <label className='col-sm-5 control-label visible-xs-block'>
</label>
- <div className='col-sm-7'>
+ <div className='col-sm-12'>
<input
className='form-control'
type='text'
diff --git a/web/react/components/team_signup_email_item.jsx b/web/react/components/team_signup_email_item.jsx
index feb70dc71..790ec2e5d 100644
--- a/web/react/components/team_signup_email_item.jsx
+++ b/web/react/components/team_signup_email_item.jsx
@@ -83,4 +83,4 @@ TeamSignupEmailItem.propTypes = {
email: React.PropTypes.string
};
-export default injectIntl(TeamSignupEmailItem); \ No newline at end of file
+export default injectIntl(TeamSignupEmailItem, {withRef: true});
diff --git a/web/react/components/team_signup_send_invites_page.jsx b/web/react/components/team_signup_send_invites_page.jsx
index 46a6bc68e..343db13e8 100644
--- a/web/react/components/team_signup_send_invites_page.jsx
+++ b/web/react/components/team_signup_send_invites_page.jsx
@@ -33,8 +33,8 @@ export default class TeamSignupSendInvitesPage extends React.Component {
var emails = [];
for (var i = 0; i < this.props.state.invites.length; i++) {
- if (this.refs['email_' + i].validate(this.props.state.team.email)) {
- emails.push(this.refs['email_' + i].getValue());
+ if (this.refs['email_' + i].getWrappedInstance().validate(this.props.state.team.email)) {
+ emails.push(this.refs['email_' + i].getWrappedInstance().getValue());
} else {
valid = false;
}
diff --git a/web/react/components/user_settings/manage_command_hooks.jsx b/web/react/components/user_settings/manage_command_hooks.jsx
index f4009aeaa..bd0659a47 100644
--- a/web/react/components/user_settings/manage_command_hooks.jsx
+++ b/web/react/components/user_settings/manage_command_hooks.jsx
@@ -257,7 +257,7 @@ export default class ManageCommandCmds extends React.Component {
let triggerDiv;
if (cmd.trigger && cmd.trigger.length !== 0) {
triggerDiv = (
- <div className='padding-top'>
+ <div className='padding-top x2'>
<strong>
<FormattedMessage
id='user.settings.cmds.trigger'
@@ -371,7 +371,7 @@ export default class ManageCommandCmds extends React.Component {
/>
</a>
<a
- className='webcmd__remove'
+ className='webhook__remove webcmd__remove'
href='#'
onClick={this.removeCmd.bind(this, cmd.id)}
>
diff --git a/web/react/components/user_settings/manage_incoming_hooks.jsx b/web/react/components/user_settings/manage_incoming_hooks.jsx
index c6532b018..e79ec6f6c 100644
--- a/web/react/components/user_settings/manage_incoming_hooks.jsx
+++ b/web/react/components/user_settings/manage_incoming_hooks.jsx
@@ -183,7 +183,7 @@ export default class ManageIncomingHooks extends React.Component {
<div key='addIncomingHook'>
<FormattedHTMLMessage
id='user.settings.hooks_in.description'
- defaultMessage='Create webhook URLs for use in external integrations. Please see<a href="http://mattermost.org/webhooks" target="_blank">http://mattermost.org/webhooks</a> to learn more.'
+ defaultMessage='Create webhook URLs for use in external integrations. Please see <a href="http://mattermost.org/webhooks" target="_blank">http://mattermost.org/webhooks</a> to learn more.'
/>
<div><label className='control-label padding-top x2'>
<FormattedMessage
diff --git a/web/react/components/user_settings/manage_outgoing_hooks.jsx b/web/react/components/user_settings/manage_outgoing_hooks.jsx
index 3f88e9f41..44aab486e 100644
--- a/web/react/components/user_settings/manage_outgoing_hooks.jsx
+++ b/web/react/components/user_settings/manage_outgoing_hooks.jsx
@@ -18,6 +18,10 @@ const holders = defineMessages({
callbackHolder: {
id: 'user.settings.hooks_out.callbackHolder',
defaultMessage: 'Each URL must start with http:// or https://'
+ },
+ select: {
+ id: 'user.settings.hooks_out.select',
+ defaultMessage: '--- Select a channel ---'
}
});
@@ -153,10 +157,7 @@ class ManageOutgoingHooks extends React.Component {
key='select-channel'
value=''
>
- <FormattedMessage
- id='user.settings.hooks_out.select'
- defaultMessage='--- Select a channel ---'
- />
+ {this.props.intl.formatMessage(holders.select)}
</option>
);
diff --git a/web/react/components/user_settings/user_settings_modal.jsx b/web/react/components/user_settings/user_settings_modal.jsx
index a7541073e..5442f7ac4 100644
--- a/web/react/components/user_settings/user_settings_modal.jsx
+++ b/web/react/components/user_settings/user_settings_modal.jsx
@@ -7,6 +7,7 @@ import SettingsSidebar from '../settings_sidebar.jsx';
import UserStore from '../../stores/user_store.jsx';
import * as Utils from '../../utils/utils.jsx';
+import Constants from '../../utils/constants.jsx';
const Modal = ReactBootstrap.Modal;
@@ -224,14 +225,19 @@ class UserSettingsModal extends React.Component {
resetTheme() {
const user = UserStore.getCurrentUser();
- if (user.theme_props != null) {
+ if (user.theme_props == null) {
+ Utils.applyTheme(Constants.THEMES.default);
+ } else {
Utils.applyTheme(user.theme_props);
}
}
render() {
const {formatMessage} = this.props.intl;
+ var currentUser = UserStore.getCurrentUser();
+ var isAdmin = Utils.isAdmin(currentUser.roles);
var tabs = [];
+
tabs.push({name: 'general', uiName: formatMessage(holders.general), icon: 'glyphicon glyphicon-cog'});
tabs.push({name: 'security', uiName: formatMessage(holders.security), icon: 'glyphicon glyphicon-lock'});
tabs.push({name: 'notifications', uiName: formatMessage(holders.notifications), icon: 'glyphicon glyphicon-exclamation-sign'});
@@ -240,8 +246,17 @@ class UserSettingsModal extends React.Component {
}
if (global.window.mm_config.EnableIncomingWebhooks === 'true' || global.window.mm_config.EnableOutgoingWebhooks === 'true' || global.window.mm_config.EnableCommands === 'true') {
- tabs.push({name: 'integrations', uiName: formatMessage(holders.integrations), icon: 'glyphicon glyphicon-transfer'});
+ var show = global.window.mm_config.EnableOnlyAdminIntegrations !== 'true';
+
+ if (global.window.mm_config.EnableOnlyAdminIntegrations === 'true' && isAdmin) {
+ show = true;
+ }
+
+ if (show) {
+ tabs.push({name: 'integrations', uiName: formatMessage(holders.integrations), icon: 'glyphicon glyphicon-transfer'});
+ }
}
+
tabs.push({name: 'display', uiName: formatMessage(holders.display), icon: 'glyphicon glyphicon-eye-open'});
tabs.push({name: 'advanced', uiName: formatMessage(holders.advanced), icon: 'glyphicon glyphicon-list-alt'});
diff --git a/web/react/components/user_settings/user_settings_security.jsx b/web/react/components/user_settings/user_settings_security.jsx
index 5693047c2..53d79906f 100644
--- a/web/react/components/user_settings/user_settings_security.jsx
+++ b/web/react/components/user_settings/user_settings_security.jsx
@@ -11,6 +11,7 @@ import TeamStore from '../../stores/team_store.jsx';
import * as Client from '../../utils/client.jsx';
import * as AsyncClient from '../../utils/async_client.jsx';
+import * as Utils from '../../utils/utils.jsx';
import Constants from '../../utils/constants.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
@@ -216,15 +217,12 @@ class SecurityTab extends React.Component {
var describe;
var d = new Date(this.props.user.last_password_update);
- var timeOfDay = ' am';
- if (d.getHours() >= 12) {
- timeOfDay = ' pm';
- }
const locale = global.window.mm_locale;
+ const hours12 = !Utils.isMilitaryTime();
describe = formatMessage(holders.lastUpdated, {
date: d.toLocaleDateString(locale, {month: 'short', day: '2-digit', year: 'numeric'}),
- time: d.toLocaleTimeString(locale, {hours12: true, hour: '2-digit', minute: '2-digit'}) + timeOfDay
+ time: d.toLocaleTimeString(locale, {hour12: hours12, hour: '2-digit', minute: '2-digit'})
});
updateSectionStatus = function updateSection() {
diff --git a/web/react/package.json b/web/react/package.json
index fce3e6555..5fc2f2851 100644
--- a/web/react/package.json
+++ b/web/react/package.json
@@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"autolinker": "0.22.0",
+ "fastclick": "^1.0.6",
"flux": "2.1.1",
"highlight.js": "8.9.1",
"keymirror": "0.1.1",
diff --git a/web/react/stores/channel_store.jsx b/web/react/stores/channel_store.jsx
index ac800a988..60cb10de7 100644
--- a/web/react/stores/channel_store.jsx
+++ b/web/react/stores/channel_store.jsx
@@ -308,7 +308,7 @@ ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
ChannelStore.storeChannels(action.channels);
ChannelStore.storeChannelMembers(action.members);
currentId = ChannelStore.getCurrentId();
- if (currentId && !document.hidden) {
+ if (currentId && window.isActive) {
ChannelStore.resetCounts(currentId);
}
ChannelStore.setUnreadCounts();
@@ -321,7 +321,7 @@ ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
ChannelStore.pStoreChannelMember(action.member);
}
currentId = ChannelStore.getCurrentId();
- if (currentId && !document.hidden) {
+ if (currentId && window.isActive) {
ChannelStore.resetCounts(currentId);
}
ChannelStore.setUnreadCount(action.channel.id);
diff --git a/web/react/stores/post_store.jsx b/web/react/stores/post_store.jsx
index b5bb93576..f5c342163 100644
--- a/web/react/stores/post_store.jsx
+++ b/web/react/stores/post_store.jsx
@@ -83,8 +83,6 @@ class PostStoreClass extends EventEmitter {
this.getCommentDraft = this.getCommentDraft.bind(this);
this.clearDraftUploads = this.clearDraftUploads.bind(this);
this.clearCommentDraftUploads = this.clearCommentDraftUploads.bind(this);
- this.storeLatestUpdate = this.storeLatestUpdate.bind(this);
- this.getLatestUpdate = this.getLatestUpdate.bind(this);
this.getCurrentUsersLatestPost = this.getCurrentUsersLatestPost.bind(this);
this.getCommentCount = this.getCommentCount.bind(this);
@@ -258,7 +256,7 @@ class PostStoreClass extends EventEmitter {
const np = newPosts.posts[pid];
if (np.delete_at === 0) {
combinedPosts.posts[pid] = np;
- if (combinedPosts.order.indexOf(pid) === -1) {
+ if (combinedPosts.order.indexOf(pid) === -1 && newPosts.order.indexOf(pid) !== -1) {
combinedPosts.order.push(pid);
}
}
@@ -507,19 +505,6 @@ class PostStoreClass extends EventEmitter {
}
});
}
- storeLatestUpdate(channelId, time) {
- if (!this.postsInfo.hasOwnProperty(channelId)) {
- this.postsInfo[channelId] = {};
- }
- this.postsInfo[channelId].latestPost = time;
- }
- getLatestUpdate(channelId) {
- if (this.postsInfo.hasOwnProperty(channelId) && this.postsInfo[channelId].hasOwnProperty('latestPost')) {
- return this.postsInfo[channelId].latestPost;
- }
-
- return 0;
- }
getCommentCount(post) {
const posts = this.getAllPosts(post.channel_id).posts;
diff --git a/web/react/stores/socket_store.jsx b/web/react/stores/socket_store.jsx
index e1b65fe14..efb57e226 100644
--- a/web/react/stores/socket_store.jsx
+++ b/web/react/stores/socket_store.jsx
@@ -32,6 +32,8 @@ class SocketStoreClass extends EventEmitter {
this.failCount = 0;
+ this.translations = this.getDefaultTranslations();
+
this.initialize();
}
@@ -174,6 +176,18 @@ class SocketStoreClass extends EventEmitter {
this.translations = messages;
}
+ getDefaultTranslations() {
+ return ({
+ socketError: 'Please check connection, Mattermost unreachable. If issue persists, ask administrator to check WebSocket port.',
+ someone: 'Someone',
+ posted: 'Posted',
+ uploadedImage: ' uploaded an image',
+ uploadedFile: ' uploaded a file',
+ something: ' did something new',
+ wrote: ' wrote: '
+ });
+ }
+
close() {
if (conn && conn.readyState === WebSocket.OPEN) {
conn.close();
@@ -188,10 +202,10 @@ function handleNewPostEvent(msg, translations) {
// Update channel state
if (ChannelStore.getCurrentId() === msg.channel_id) {
- if (document.hidden) {
- AsyncClient.getChannel(msg.channel_id);
- } else {
+ if (window.isActive) {
AsyncClient.updateLastViewedAt();
+ } else {
+ AsyncClient.getChannel(msg.channel_id);
}
} else if (UserStore.getCurrentId() !== msg.user_id || post.type !== Constants.POST_TYPE_JOIN_LEAVE) {
AsyncClient.getChannel(msg.channel_id);
diff --git a/web/react/utils/async_client.jsx b/web/react/utils/async_client.jsx
index c8676f45d..13b57092d 100644
--- a/web/react/utils/async_client.jsx
+++ b/web/react/utils/async_client.jsx
@@ -521,18 +521,25 @@ export function getPosts(id) {
return;
}
- if (PostStore.getAllPosts(channelId) == null) {
+ const postList = PostStore.getAllPosts(channelId);
+
+ if ($.isEmptyObject(postList) || postList.order.length < Constants.POST_CHUNK_SIZE) {
getPostsPage(channelId, Constants.POST_CHUNK_SIZE);
return;
}
- const latestUpdate = PostStore.getLatestUpdate(channelId);
+ const latestPost = PostStore.getLatestPost(channelId);
+ let latestPostTime = 0;
+
+ if (latestPost != null && latestPost.update_at != null) {
+ latestPostTime = latestPost.create_at;
+ }
callTracker['getPosts_' + channelId] = utils.getTimestamp();
client.getPosts(
channelId,
- latestUpdate,
+ latestPostTime,
(data, textStatus, xhr) => {
if (xhr.status === 304 || !data) {
return;
@@ -542,7 +549,7 @@ export function getPosts(id) {
type: ActionTypes.RECEIVED_POSTS,
id: channelId,
before: true,
- numRequested: Constants.POST_CHUNK_SIZE,
+ numRequested: 0,
post_list: data
});
diff --git a/web/react/utils/markdown.jsx b/web/react/utils/markdown.jsx
index 8b3602a89..493916058 100644
--- a/web/react/utils/markdown.jsx
+++ b/web/react/utils/markdown.jsx
@@ -152,7 +152,7 @@ class MattermostMarkdownRenderer extends marked.Renderer {
}
codespan(text) {
- return '<pre class="text-nowrap">' + super.codespan(text) + '</pre>';
+ return '<span class="codespan__pre-wrap">' + super.codespan(text) + '</span>';
}
br() {
@@ -222,7 +222,7 @@ class MattermostMarkdownRenderer extends marked.Renderer {
}
table(header, body) {
- return `<table class="markdown__table"><thead>${header}</thead><tbody>${body}</tbody></table>`;
+ return `<div class="table-responsive"><table class="markdown__table"><thead>${header}</thead><tbody>${body}</tbody></table></div>`;
}
listitem(text) {
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index e2a5b9620..7f124149d 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -260,6 +260,10 @@ export function displayTimeFormatted(ticks) {
);
}
+export function isMilitaryTime() {
+ return PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time');
+}
+
export function displayDateTime(ticks) {
var seconds = Math.floor((Date.now() - ticks) / 1000);
diff --git a/web/sass-files/sass/partials/_markdown.scss b/web/sass-files/sass/partials/_markdown.scss
index a08379ae1..f34b5ec19 100644
--- a/web/sass-files/sass/partials/_markdown.scss
+++ b/web/sass-files/sass/partials/_markdown.scss
@@ -31,6 +31,7 @@
}
.post-body--code__language {
+ -webkit-transform: translate3d(0,0,0);
position: absolute;
top: 0;
right: 0;
@@ -54,11 +55,9 @@
code {
white-space: pre;
}
- pre {
- &.text-nowrap {
- code {
- white-space: nowrap;
- }
+ .codespan__pre-wrap {
+ code {
+ white-space: pre-wrap;
}
}
}
diff --git a/web/sass-files/sass/partials/_modal.scss b/web/sass-files/sass/partials/_modal.scss
index db99e840b..9d4e62bc3 100644
--- a/web/sass-files/sass/partials/_modal.scss
+++ b/web/sass-files/sass/partials/_modal.scss
@@ -7,6 +7,67 @@
padding: 20px 15px;
overflow: auto;
}
+.more-table {
+ margin: 0;
+ table-layout: fixed;
+ p {
+ font-size: 0.9em;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ @include opacity(0.8);
+ margin: 5px 0;
+ }
+ .profile-img {
+ -moz-border-radius: 50px;
+ -webkit-border-radius: 50px;
+ border-radius: 50px;
+ margin-right: 8px;
+ }
+ .more-name {
+ font-weight: 600;
+ font-size: 0.95em;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ .more-description {
+ @include opacity(0.7);
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ tbody {
+ > tr {
+ &:hover td {
+ background: #f7f7f7;
+ }
+ &:first-child {
+ td {
+ border: none;
+ }
+ }
+ td {
+ width: 100%;
+ white-space: nowrap;
+ @include legacy-pie-clearfix;
+ text-overflow: ellipsis;
+ padding: 8px 8px 8px 15px;
+ &.padding--equal {
+ padding: 8px;
+ }
+ &.td--action {
+ text-align: right;
+ padding: 8px 15px 8px 8px;
+ width: 80px;
+ vertical-align: middle;
+ position: relative;
+ &.lg {
+ width: 110px;
+ }
+ }
+ }
+ }
+ }
+}
.modal {
width: 100%;
color: #333;
@@ -148,64 +209,6 @@
padding: 0;
}
}
- .more-table {
- margin: 0;
- table-layout: fixed;
- p {
- font-size: 0.9em;
- overflow: hidden;
- text-overflow: ellipsis;
- @include opacity(0.8);
- margin: 5px 0;
- }
- .profile-img {
- -moz-border-radius: 50px;
- -webkit-border-radius: 50px;
- border-radius: 50px;
- margin-right: 8px;
- }
- .more-name {
- font-weight: 600;
- font-size: 0.95em;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .more-description {
- @include opacity(0.7);
- display: block;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- tbody {
- > tr {
- &:hover td {
- background: #f7f7f7;
- }
- &:first-child {
- td {
- border: none;
- }
- }
- td {
- width: 100%;
- white-space: nowrap;
- @include legacy-pie-clearfix;
- text-overflow: ellipsis;
- padding: 8px 8px 8px 15px;
- &.td--action {
- text-align: right;
- padding: 8px 15px 8px 8px;
- width: 80px;
- vertical-align: middle;
- position: relative;
- &.lg {
- width: 110px;
- }
- }
- }
- }
- }
- }
.modal-image {
position:relative;
width:100%;
diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss
index 5d6cbee60..2374a9dbb 100644
--- a/web/sass-files/sass/partials/_responsive.scss
+++ b/web/sass-files/sass/partials/_responsive.scss
@@ -776,6 +776,24 @@
}
}
@media screen and (max-width: 480px) {
+ .settings-modal {
+
+ .settings-table {
+
+ .security-links {
+ margin-bottom: 10px;
+ display: block;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ }
+
+ }
+
+ }
+
.tip-overlay.tip-overlay--sidebar {
min-height: 350px;
}
diff --git a/web/sass-files/sass/partials/_settings.scss b/web/sass-files/sass/partials/_settings.scss
index 1bbec566c..de7df403f 100644
--- a/web/sass-files/sass/partials/_settings.scss
+++ b/web/sass-files/sass/partials/_settings.scss
@@ -207,6 +207,7 @@
.section-title {
margin-bottom: 5px;
font-weight: 600;
+ padding-right: 50px;
}
.section-edit {
diff --git a/web/sass-files/sass/partials/_tutorial.scss b/web/sass-files/sass/partials/_tutorial.scss
index 0a2d1e704..1f93fc6a9 100644
--- a/web/sass-files/sass/partials/_tutorial.scss
+++ b/web/sass-files/sass/partials/_tutorial.scss
@@ -105,7 +105,7 @@
font-size: 12px;
span {
- @include opacity(0.5);
+ @include opacity(0.9);
}
}
diff --git a/web/static/i18n/en.json b/web/static/i18n/en.json
index ade6462e9..9fbd5c343 100644
--- a/web/static/i18n/en.json
+++ b/web/static/i18n/en.json
@@ -827,11 +827,13 @@
"rename_channel.cancel": "Cancel",
"rename_channel.save": "Save",
"rhs_comment.comment": "Comment",
+ "rhs_comment.permalink": "Permalink",
"rhs_comment.edit": "Edit",
"rhs_comment.del": "Delete",
"rhs_comment.retry": "Retry",
"rhs_header.details": "Message Details",
"rhs_root.direct": "Direct Message",
+ "rhs_root.permalink": "Permalink",
"rhs_root.edit": "Edit",
"rhs_root.del": "Delete",
"search_bar.search": "Search",
@@ -1068,33 +1070,33 @@
"user.settings.cmds.add_trigger.placeholder": "Command trigger e.g. \"hello\" not including the slash",
"user.settings.cmds.auto_complete_desc.placeholder": "Example: \"Returns search results for patient records\"",
"user.settings.cmds.auto_complete_hint.placeholder": "Example: [Patient Name]",
- "user.settings.cmds.auto_complete_desc_desc": "Optional short description of slash command for the autocomplete list.",
"user.settings.cmds.url.placeholder": "Must start with http:// or https://",
"user.settings.cmds.auto_complete.yes": "yes",
"user.settings.cmds.auto_complete.no": "no",
"user.settings.cmds.trigger": "Command Trigger Word: ",
- "user.settings.cmds.display_name": "Descriptive Label: ",
+ "user.settings.cmds.url": "Request URL: ",
+ "user.settings.cmds.request_type": "Request Method: ",
"user.settings.cmds.username": "Response Username: ",
"user.settings.cmds.icon_url": "Response Icon: ",
"user.settings.cmds.auto_complete": "Autocomplete: ",
- "user.settings.cmds.auto_complete_desc": "Autocomplete Description: ",
"user.settings.cmds.auto_complete_hint": "Autocomplete Hint: ",
- "user.settings.cmds.request_type": "Request Method: ",
- "user.settings.cmds.url": "Request URL: ",
+ "user.settings.cmds.auto_complete_desc": "Autocomplete Description: ",
+ "user.settings.cmds.display_name": "Descriptive Label: ",
"user.settings.cmds.token": "Token: ",
"user.settings.cmds.regen": "Regen Token",
"user.settings.cmds.none": "None",
"user.settings.cmds.existing": "Existing commands",
- "user.settings.cmds.add_desc": "Create slash commands to send events to external integrations and receive a response. For example typing `/patient Joe Smith` could bring back search results from your internal health records management system for the name 'Joe Smith'. Please see <a href=\"http://docs.mattermost.com/developer/slash-commands.html\">Slash commands documentation</a> for detailed instructions.",
+ "user.settings.cmds.add_desc": "Create slash commands to send events to external integrations and receive a response. For example typing `/patient Joe Smith` could bring back search results from your internal health records management system for the name “Joe Smith”. Please see <a href=\"http://docs.mattermost.com/developer/slash-commands.html\">Slash commands documentation</a> for detailed instructions.",
"user.settings.cmds.add_new": "Add a new command",
- "user.settings.cmds.cmd_display_name": "Brief description of slash command to show in listings.",
+ "user.settings.cmds.trigger_desc": "Examples: /patient, /client, /employee Reserved: /echo, /join, /logout, /me, /shrug",
+ "user.settings.cmds.url_desc": "The callback URL to receive the HTTP POST or GET event request when the slash command is run.",
+ "user.settings.cmds.request_type_desc": "The type of command request issued to the Request URL.",
"user.settings.cmds.username_desc": "Choose a username override for responses for this slash command. Usernames can consist of up to 22 characters consisting of lowercase letters, numbers and they symbols \"-\", \"_\", and \".\" .",
"user.settings.cmds.icon_url_desc": "Choose a profile picture override for the post responses to this slash command. Enter the URL of a .png or .jpg file at least 128 pixels by 128 pixels.",
- "user.settings.cmds.trigger_desc": "Examples: /patient, /client, /employee Reserved: /echo, /join, /logout, /me, /shrug",
"user.settings.cmds.auto_complete_help": " Show this command in the autocomplete list.",
"user.settings.cmds.auto_complete_hint_desc": "Optional hint in the autocomplete list about parameters needed for command.",
- "user.settings.cmds.request_type_desc": "The type of command request issued to the Request URL.",
- "user.settings.cmds.url_desc": "The callback URL to receive the HTTP POST or GET event request when the slash command is run.",
+ "user.settings.cmds.auto_complete_desc_desc": "Optional short description of slash command for the autocomplete list.",
+ "user.settings.cmds.cmd_display_name": "Brief description of slash command to show in listings.",
"user.settings.cmds.add": "Add",
"user.settings.hooks_in.channel": "Channel: ",
"user.settings.hooks_in.none": "None",
@@ -1263,4 +1265,4 @@
"intro_messages.beginning": "Beginning of {name}",
"intro_messages.invite": "Invite others to this {type}",
"intro_messages.setHeader": "Set a Header"
-}
+} \ No newline at end of file
diff --git a/web/static/i18n/es.json b/web/static/i18n/es.json
index e4ddd76ce..c44545e67 100644
--- a/web/static/i18n/es.json
+++ b/web/static/i18n/es.json
@@ -674,6 +674,8 @@
"get_link.clipboard": " Enlace copiado al portapapeles.",
"get_link.close": "Cerrar",
"get_link.copy": "Copiar Enlace",
+ "get_post_link_modal.help": "En enlace de abajo permite a los usuarios autorizados a ver tu mensaje.",
+ "get_post_link_modal.title": "Copiar enlace Permanente",
"get_team_invite_link_modal.help": "Enviar a los compañeros de equipo el enlace que se muestra a continuación para permitirles registrarse a este equipo.",
"get_team_invite_link_modal.helpDisabled": "La creación de usuario ha sido deshabilitada para tu equipo. Por favor solicita más detalles a tu administrador de equipo.",
"get_team_invite_link_modal.title": "Enlace de Invitación al Equipo",
@@ -1058,19 +1060,40 @@
"user.settings.advance.sendTitle": "Enviar mensajes con Ctrl + Retorno",
"user.settings.advance.title": "Configuración Avanzada",
"user.settings.cmds.add": "Agregar",
+ "user.settings.cmds.add_desc": "Crea comandos de barra para enviar eventos a integraciones externas y recibir una respuesta. Por ejemplo al escribir `/paciente Joe Smith` podría retornar resultados de una búsqueda en tu sistema de adminitración de salud para el nombre “Joe Smith”. Por favor revisa la <a href=\"http://docs.mattermost.com/developer/slash-commands.html\">Documentación de comandos de barra</a> para instrucciones detallas.",
+ "user.settings.cmds.add_display_name.placeholder": "Ejemplo: \"Buscar registros del paciente\"",
"user.settings.cmds.add_new": "Agregar un nuevo comando",
"user.settings.cmds.add_trigger.placeholder": "Gatillador del Comando ej. \"hola\" no se debe incluir la barra",
"user.settings.cmds.add_username.placeholder": "Nombre de usuario",
+ "user.settings.cmds.auto_complete": "Autocompletado: ",
"user.settings.cmds.auto_complete.no": "no",
"user.settings.cmds.auto_complete.yes": "sí",
+ "user.settings.cmds.auto_complete_desc": "Descripción del Autocompletado: ",
+ "user.settings.cmds.auto_complete_desc.placeholder": "Ejemplo: \"Retorna resultados de una búsqueda con los registros de un paciente\"",
+ "user.settings.cmds.auto_complete_desc_desc": "Descripción corta opcional para la lista de autocompletado del comando de barra.",
"user.settings.cmds.auto_complete_help": "Mostrar este comando en la lista de auto completado.",
+ "user.settings.cmds.auto_complete_hint": "Pista del Autocompletado: ",
+ "user.settings.cmds.auto_complete_hint.placeholder": "Ejemplo: [Nombre del Paciente]",
+ "user.settings.cmds.auto_complete_hint_desc": "Pista opcional que aparece como paramentros necesarios en la lista de autocompletado para el comando.",
+ "user.settings.cmds.cmd_display_name": "Breve descripción del comando de barra para mostrar en el listado.",
+ "user.settings.cmds.display_name": "Etiqueta Descriptiva: ",
"user.settings.cmds.existing": "Comandos existentes",
+ "user.settings.cmds.icon_url": "Icono de Respuesta: ",
+ "user.settings.cmds.icon_url_desc": "Escoge una imagen de perfil que reemplazara los mensajes publicados por este comando de barra. Ingresa el URL de un archivo .png o .jpg de al menos 128 x 128 pixels.",
"user.settings.cmds.none": "Ninguno",
"user.settings.cmds.regen": "Regenerar Token",
+ "user.settings.cmds.request_type": "Método de Solicitud: ",
+ "user.settings.cmds.request_type_desc": "El tipo de comando que se utiliza al hacer una solicitud al URL.",
"user.settings.cmds.request_type_get": "GET",
"user.settings.cmds.request_type_post": "POST",
"user.settings.cmds.token": "Token: ",
+ "user.settings.cmds.trigger": "Palabra Gatilladora del Comando: ",
+ "user.settings.cmds.trigger_desc": "Ejemplos: /paciente, /cliente, /empleado Reservadas: /echo, /join, /logout, /me, /shrug",
+ "user.settings.cmds.url": "URL de Solicitud: ",
"user.settings.cmds.url.placeholder": "Debe comenzar con http:// o https://",
+ "user.settings.cmds.url_desc": "El URL para recibir el evento de la solicitud HTTP POST o GET cuando se ejecuta el comando de barra.",
+ "user.settings.cmds.username": "Nombre de usuario de Respuesta: ",
+ "user.settings.cmds.username_desc": "Escoge un nombre de usuario que reemplazara los mensajes publicados por este comando de barra. Los nombres de usuario pueden tener hasta 22 caracteres y contener letras en minúsculas, números y los siguientes símbolos \"-\", \"_\", y \".\" .",
"user.settings.custom_theme.awayIndicator": "Indicador Ausente",
"user.settings.custom_theme.buttonBg": "Fondo Botón",
"user.settings.custom_theme.buttonColor": "Texto Botón",
@@ -1173,6 +1196,8 @@
"user.settings.import_theme.importHeader": "Importar Tema de Slack",
"user.settings.import_theme.submit": "Enviar",
"user.settings.import_theme.submitError": "Formato inválido, por favor intenta copiando y pegando nuevamente.",
+ "user.settings.integrations.commands": "Comandos de Barra",
+ "user.settings.integrations.commandsDescription": "Administra tus comandos de barra",
"user.settings.integrations.incomingWebhooks": "Webhooks de entrada",
"user.settings.integrations.incomingWebhooksDescription": "Administra tus webhooks de entrada",
"user.settings.integrations.outWebhooks": "Webhooks de salida",
@@ -1238,4 +1263,4 @@
"view_image_popover.download": "Descargar",
"view_image_popover.file": "Archivo {count} de {total}",
"view_image_popover.publicLink": "Obtener Enlace Público"
-}
+} \ No newline at end of file
diff --git a/web/templates/head.html b/web/templates/head.html
index da65e1779..94a86e3dd 100644
--- a/web/templates/head.html
+++ b/web/templates/head.html
@@ -79,6 +79,7 @@
$(function() {
if (window.mm_preferences != null) {
PreferenceStore.setPreferences(window.mm_preferences);
+ PreferenceStore.emitChange();
}
});