summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md4
-rw-r--r--api/command.go73
-rw-r--r--api/command_test.go56
-rw-r--r--api/post.go6
-rw-r--r--doc/README.md20
-rw-r--r--doc/help/Account-Settings.md106
-rw-r--r--doc/help/Markdown.md180
-rw-r--r--doc/help/README.md18
-rw-r--r--doc/process/overview.md2
-rw-r--r--doc/usage/Markdown.md179
-rw-r--r--web/react/components/channel_header.jsx8
-rw-r--r--web/react/components/command_list.jsx2
-rw-r--r--web/react/components/member_list_team.jsx47
-rw-r--r--web/react/components/mention_list.jsx12
-rw-r--r--web/react/components/navbar_dropdown.jsx10
-rw-r--r--web/react/components/rhs_root_post.jsx82
-rw-r--r--web/react/components/sidebar_right_menu.jsx11
-rw-r--r--web/react/components/team_members.jsx130
-rw-r--r--web/react/components/team_members_modal.jsx69
-rw-r--r--web/react/components/user_settings/user_settings_general.jsx4
-rw-r--r--web/react/components/user_settings/user_settings_notifications.jsx10
-rw-r--r--web/react/pages/channel.jsx6
-rw-r--r--web/react/utils/constants.jsx3
23 files changed, 634 insertions, 404 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b8f2faf81..595371fe6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -43,6 +43,8 @@ Many thanks to our external contributors. In no particular order:
- [jdhoek](https://github.com/jdhoek)
- [Tsynapse](https://github.com/Tsynapse)
- [alexgaribay](https://github.com/alexgaribay)
+- [vladikoff](https://github.com/vladikoff)
+- [jonathanwiesel](https://github.com/jonathanwiesel)
## Release v1.2.1
@@ -69,7 +71,7 @@ To limit the impact of this security issue, Mattermost v1.2.0 has been removed f
#### Syntax Highlighting
-- Syntax highlight for code blocks now available for `Diff, Apache, Makefile, HTTP, JSON, Markdown, Java, CSS, nginx, ObjectiveC, Python, XML, Perl, Bash, PHP, Coffee, C, SQL, Go, Ruby, Java, and ini`
+- Syntax highlight for code blocks now available for `Diff, Apache, Makefile, HTTP, JSON, Markdown, JavaScript, CSS, nginx, ObjectiveC, Python, XML, Perl, Bash, PHP, Coffee, C, SQL, Go, Ruby, Java, and ini`
#### Usability Improvements
diff --git a/api/command.go b/api/command.go
index 50ca41155..db57f0bae 100644
--- a/api/command.go
+++ b/api/command.go
@@ -4,7 +4,9 @@
package api
import (
+ "io"
"net/http"
+ "path"
"strconv"
"strings"
"time"
@@ -325,6 +327,9 @@ func loadTestCommand(c *Context, command *model.Command) bool {
if loadTestPostsCommand(c, command) {
return true
}
+ if loadTestUrlCommand(c, command) {
+ return true
+ }
} else if strings.Index(cmd, command.Command) == 0 {
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: "Debug Load Testing"})
}
@@ -571,3 +576,71 @@ func loadTestPostsCommand(c *Context, command *model.Command) bool {
return false
}
+
+func loadTestUrlCommand(c *Context, command *model.Command) bool {
+ cmd := cmds["loadTestCommand"] + " url"
+
+ if strings.Index(command.Command, cmd) == 0 && !command.Suggest {
+ url := ""
+
+ parameters := strings.SplitN(command.Command, " ", 3)
+ if len(parameters) != 3 {
+ c.Err = model.NewAppError("loadTestUrlCommand", "Command must contain a url", "")
+ return true
+ } else {
+ url = parameters[2]
+ }
+
+ // provide a shortcut to easily access tests stored in doc/developer/tests
+ if !strings.HasPrefix(url, "http") {
+ url = "https://raw.githubusercontent.com/mattermost/platform/master/doc/developer/tests/" + url
+
+ if path.Ext(url) == "" {
+ url += ".md"
+ }
+ }
+
+ var contents io.ReadCloser
+ if r, err := http.Get(url); err != nil {
+ c.Err = model.NewAppError("loadTestUrlCommand", "Unable to get file", err.Error())
+ return false
+ } else if r.StatusCode > 400 {
+ c.Err = model.NewAppError("loadTestUrlCommand", "Unable to get file", r.Status)
+ return false
+ } else {
+ contents = r.Body
+ }
+
+ bytes := make([]byte, 4000)
+
+ // break contents into 4000 byte posts
+ for {
+ length, err := contents.Read(bytes)
+ if err != nil && err != io.EOF {
+ c.Err = model.NewAppError("loadTestUrlCommand", "Encountered error reading file", err.Error())
+ return false
+ }
+
+ if length == 0 {
+ break
+ }
+
+ post := &model.Post{}
+ post.Message = string(bytes[:length])
+ post.ChannelId = command.ChannelId
+
+ if _, err := CreatePost(c, post, false); err != nil {
+ l4g.Error("Unable to create post, err=%v", err)
+ return false
+ }
+ }
+
+ command.Response = model.RESP_EXECUTED
+
+ return true
+ } else if strings.Index(cmd, command.Command) == 0 && strings.Index(command.Command, "/loadtest posts") != 0 {
+ command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: "Add a post containing the text from a given url to current channel <Url>"})
+ }
+
+ return false
+}
diff --git a/api/command_test.go b/api/command_test.go
index 476748c6b..9327850f3 100644
--- a/api/command_test.go
+++ b/api/command_test.go
@@ -10,6 +10,7 @@ import (
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
+ "github.com/mattermost/platform/utils"
)
func TestSuggestRootCommands(t *testing.T) {
@@ -184,3 +185,58 @@ func TestEchoCommand(t *testing.T) {
t.Fatal("Echo command failed to send")
}
}
+
+func TestLoadTestUrlCommand(t *testing.T) {
+ Setup()
+
+ // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
+ enableTesting := utils.Cfg.ServiceSettings.EnableTesting
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableTesting = enableTesting
+ }()
+
+ utils.Cfg.ServiceSettings.EnableTesting = true
+
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user.Id))
+
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
+
+ channel := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
+
+ command := "/loadtest url "
+ if _, err := Client.Command(channel.Id, command, false); err == nil {
+ t.Fatal("/loadtest url with no url should've failed")
+ }
+
+ command = "/loadtest url http://www.hopefullynonexistent.file/path/asdf/qwerty"
+ if _, err := Client.Command(channel.Id, command, false); err == nil {
+ t.Fatal("/loadtest url with invalid url should've failed")
+ }
+
+ command = "/loadtest url https://raw.githubusercontent.com/mattermost/platform/master/README.md"
+ if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.Command); r.Response != model.RESP_EXECUTED {
+ t.Fatal("/loadtest url for README.md should've executed")
+ }
+
+ command = "/loadtest url test-emoticons.md"
+ if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.Command); r.Response != model.RESP_EXECUTED {
+ t.Fatal("/loadtest url for test-emoticons.md should've executed")
+ }
+
+ command = "/loadtest url test-emoticons"
+ if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.Command); r.Response != model.RESP_EXECUTED {
+ t.Fatal("/loadtest url for test-emoticons should've executed")
+ }
+
+ posts := Client.Must(Client.GetPosts(channel.Id, 0, 5, "")).Data.(*model.PostList)
+ // note that this may make more than 3 posts if files are too long to fit in an individual post
+ if len(posts.Order) < 3 {
+ t.Fatal("/loadtest url made too few posts, perhaps there needs to be a delay before GetPosts in the test?")
+ }
+}
diff --git a/api/post.go b/api/post.go
index ca99eb15b..88d0127d3 100644
--- a/api/post.go
+++ b/api/post.go
@@ -407,9 +407,9 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
}
// Add @all to keywords if user has them turned on
- if profile.NotifyProps["all"] == "true" {
- keywordMap["@all"] = append(keywordMap["@all"], profile.Id)
- }
+ // if profile.NotifyProps["all"] == "true" {
+ // keywordMap["@all"] = append(keywordMap["@all"], profile.Id)
+ // }
// Add @channel to keywords if user has them turned on
if profile.NotifyProps["channel"] == "true" {
diff --git a/doc/README.md b/doc/README.md
index d062dee65..15d1d731b 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -51,22 +51,4 @@ Procedures for upgrading the Mattermost server
## Help
-_Note: End user help documentation is a new feature being completed for the v1.2 release. The materials below are work in progress._
-
-- Getting Started
- - [Sign-in](help/Sign-in.md)
-
-- User Interface
- - Main Menu
- - [Team Settings ](https://github.com/mattermost/platform/blob/help-docs-update/doc/help/Team-Settings.md)
- - [General Settings](https://github.com/mattermost/platform/blob/help-docs-update/doc/help/Team-Settings.md#general)
- - [Slack Import](https://github.com/mattermost/platform/blob/help-docs-update/doc/help/Team-Settings.md#import-from-slack-beta)
- - [Manage Members](help/Manage-Members.md)
- - Messaging
- - [Mattermost Markdown Formatting](usage/Markdown.md)
- - [Search](help/Search.md)
-
-- System Console
- - Team
- - [Team Statistics](help/system-console/Team-Statistics.md)
-
+See [End User Help](help/README.md).
diff --git a/doc/help/Account-Settings.md b/doc/help/Account-Settings.md
new file mode 100644
index 000000000..a0c55d3e9
--- /dev/null
+++ b/doc/help/Account-Settings.md
@@ -0,0 +1,106 @@
+# Account Settings
+___
+Account Settings is accessible from the **Main Menu** by clicking the three dots at the top of the channels pane. From here, you can configure your profile settings, notification preferences, integrations, theme settings, and display options.
+
+## General
+Settings to configure name, username, nickname, email and profile picture.
+
+#### Full Name
+Full names appear in the Direct Messages member list and team management modal. By default, you will receive mention notifications when someone types your first name. Entering a full name is optional.
+
+#### Username
+Usernames appear next to all posts. Pick something easy for teammates to recognize and recall. By default, you will receive mention notifications when someone types your username.
+
+####Nickname
+Nicknames appear in the Direct Messages member list and team management modal. You will not receive mention notifications when someone types your nickname unless you add it to the *Words That Trigger Mentions* in **Account Settings > Notifications**.
+
+#### Email
+Email is used for sign-in, notifications, and password reset. Email requires verification if changed. If you are signing in using a single sign-on service, the email field is not editable and you will receive email notifications to the email you used to sign up to your SSO service.
+
+#### Profile Picture
+Profile pictures appear next to all posts and in the top of the channels pane next to your name. All users have a generic profile picture when they sign up for an account. Edit your profile picture by clicking **Select** and then choosing a picture in either JPG or PNG format that’s at least 128px wide and 128px high. For best results, choose an image that is square with the subject of interest centered. If you edit your profile picture, all past posts will appear with the new picture.
+
+##Security
+Settings to configure your password, view access history, and view or logout of active sessions.
+
+#### Password
+You may change your password if you’ve logged in by email. If you are signing in using a single sign-on service, the password field is not editable, and you must access your SSO service’s account settings to update your password.
+
+#### View Access History
+Access History displays a chronological list of the last 20 login and logout attempts, channel creations and deletions, account settings changes, or channel setting modifications made on your account. Click **More Info** to view the IP address and session ID of each action.
+
+#### View and Logout of Active Sessions
+Sessions are created when you log in with your email and password to a new browser on a device. Sessions let you use Mattermost for up to 30 days without having to log in again. Click **Logout** on an active session if you want to revoke automatic login privileges for a specific browser or device. Click **More Info** to view details on browser and operating system.
+
+## Notifications
+Settings to configure desktop notifications, desktop notification sounds, email notifications, and words that trigger mentions.
+
+#### Send Desktop Notifications
+Desktop notifications appear at the bottom right corner of your main monitor. The desktop notification preference you choose in *Account Settings* applies globally, but this preference is customizable for each channel from the channel name drop-down menu. Desktop notifications are available on Firefox, Safari, and Chrome.
+
+#### Desktop Notification Sounds
+A notification sound plays for all Mattermost posts that would fire a desktop notification, unless *Desktop Notification Sound* is turned off. Desktop notification sounds are available on Firefox, Safari, Chrome, Internet Explorer, and Edge.
+
+#### Email Notifications
+Email notifications are sent for mentions and direct messages after you’ve been offline for more than 60 seconds or away from Mattermost for more than 5 minutes. Change the email where notifications are sent from **Account Settings > General > Email**.
+
+#### Words That Trigger Mentions
+By default, you will receive mention notifications from your non-case sensitive username, mentioned @username, @all, and @channel. Customize the words that trigger mentions by typing them in the input box. This is useful if you want to be notified of all posts on a certain topic, for example, “marketing”.
+
+## Appearance
+Settings to customize your account’s theme colors and code theme.
+
+#### Theme Colours
+Select **Theme Colors** to select from four standard themes designed by the Mattermost team. To make custom adjustments on the four standard theme colours, click a standard theme and then select **Custom Theme** to load the standard theme into the custom theme color selectors.
+
+#### Custom Theme
+Customize your theme colors and share them with others by copying and pasting theme vectors into the input box. Observe a live preview as you customize theme colors and then click **Save** to confirm your changes. Discard your changes by exiting the settings modal and clicking **Yes, Discard**.
+
+- **Sidebar BG:** Background color of the Channels pane, and Account and Team settings navigation sidebars.
+- **Sidebar Text:** Text colour of read channels in the Channels pane, and tabs in the Account and Team settings navigation sidebar.
+- **Sidebar Header BG:** Background color of the header above the Channels pane and all modal headers.
+- **Sidebar Header Text:** Text colour of the header above the Channels pane and all modal headers.
+- **Sidebar Unread Text:** Text color of unread channels in the Channels pane.
+- **Sidebar Text Hover BG:** Background color behind channel names and settings tabs as you hover over them.
+- **Sidebar Text Active Border:** Color of the rectangular marker on the left side of the Channels pane or Settings sidebar indicating the active channel or tab.
+- **Sidebar Text Active Color:** Text color of the active active channel or tab in the Channels pane or Settings sidebar.
+- **Online Indicator:** Color of the online indicator appearing next to team members names in the Direct Messages list.
+- **Mention Jewel BG:** Background color of the jewel indicating unread mentions that appears to the right of the channel name. This is also the background color of the “Unread Posts Below/Above” indicator appearing at the top or bottom of the Channels pane on shorter browser windows.
+- **Mention Jewel Text:** Text color on the mention jewel indicating the number of unread mentions. This is also the text color on the “Unread Posts Below/Above” indicator.
+- **Center Channel BG:** Color of the center pane, RHS and all modal backgrounds.
+- **Center Channel Text:** Color of all the text - with the exception of mentions, links, hashtags and code blocks - in the center pane, RHS and modals.
+- **New Message Separator:** The new massage separator appears below the last read message when you click into a channel with unread messages.
+- **Link Color:** Text color of all links, hashtags, teammate mentions, and low priority UI buttons.
+- **Button BG:** Color of the rectangular background behind all high priority UI buttons.
+- **Button Text:** Text colour appearing on the rectangular background for all high priority UI buttons.
+- **Mention Highlight BG:** Highlight color behind your words that trigger mentions in the center pane and RHS.
+- **Mention Highlight Link:** Text color of your words that trigger mentions in the center pane and RHS.
+- **Code Theme:** Background and syntax colors for all code blocks.
+
+#### Import theme colors from Slack
+To import a theme, go to **Preferences > Sidebar Theme** from within Slack, open the custom theme option, copy the theme color vector and then paste it into the *Input Slack Theme* input box in Mattermost. Any theme settings that are not customizable in Slack will default to the “Mattermost” standard theme settings.
+
+## Integrations
+Settings to configure incoming and outgoing webhooks for your team.
+
+#### Incoming Webhooks
+Incoming webhooks from external integrations can post messages to Mattermost in public channels or private groups. Learn more about setting up incoming webhooks on our [documentation page](https://github.com/mattermost/platform/blob/master/doc/integrations/webhooks/Incoming-Webhooks.md).
+
+
+#### Outgoing Webhooks
+Outgoing webhooks use trigger words to fire new message events to external integrations. For security reasons, outgoing webhooks are only available in public channels. Learn more about setting up outgoing webhooks on our [documentation page](https://github.com/mattermost/platform/blob/master/doc/integrations/webhooks/Outgoing-Webhooks.md).
+
+##Display
+Settings to configure clock and teammate name display preferences.
+
+#### Clock Display
+Choose a 12-hour or 24-hour time preference that appears on the time stamp for all posts.
+
+#### Teammate Name Display
+Configure how names are displayed in the Direct Messages list in the Channels pane: nickname, username or full name.
+
+## Advanced
+Setting to configure when messages are sent.
+
+#### Send Messages on Ctrl+Enter
+If enabled, press **Enter** to insert a new line and **Ctrl + Enter** posts the message. If disabled, **Shift + Enter** inserts a new line and **Enter** posts the message.
diff --git a/doc/help/Markdown.md b/doc/help/Markdown.md
new file mode 100644
index 000000000..1befed8d4
--- /dev/null
+++ b/doc/help/Markdown.md
@@ -0,0 +1,180 @@
+# Markdown Help
+
+Markdown makes it easy to format messages. Type a message as you normally would, and use these rules to render it with special formatting.
+
+## Text Style:
+
+You can use either `_` or `*` around a word to make it italic. Use two to make it bold.
+
+* `_italics_` renders as _italics_
+* `**bold**` renders as **bold**
+* `**_bold-italic_**` renders as **_bold-italics_**
+* `~~strikethrough~~` renders as ~~strikethrough~~
+
+## Code Block:
+
+Create a code block by indenting each line by four spaces, or by placing ``` on the line above and below your code.
+
+Example:
+
+ ```
+ code block
+ ```
+
+Renders as:
+```
+code block
+```
+
+### Syntax Highlighting
+
+To add syntax highlighting, type the language to be highlighted after the ``` at the beginning of the code block.
+
+Supported languages are:
+`diff, apache, makefile, http, json, markdown, javascript, css, nginx, objectivec, python, xml, perl, bash, php, coffee (CoffeeScript), cs (C#), cpp (C++), sql, go, ruby, java, ini, latex`
+
+Example:
+
+ ``` go
+ package main
+ import "fmt"
+ func main() {
+ fmt.Println("Hello, 世界")
+ }
+ ```
+
+Renders as:
+``` go
+package main
+import "fmt"
+func main() {
+ fmt.Println("Hello, 世界")
+}
+```
+
+## In-line Code:
+
+Create in-line monospaced font by surrounding it with backticks.
+```
+`monospace`
+```
+Renders as: `monospace`.
+
+## Links:
+
+Create labeled links by putting the desired text in square brackets and the associated link in normal brackets.
+
+`[Check out Mattermost!](www.mattermost.com)`
+
+Renders as: [Check out Mattermost!](www.mattermost.com)
+
+## In-line Images
+
+Create in-line images using an `!` followed by the alt text in square brackets and the link in normal brackets. Add hover text by placing it in quotes after the link.
+```
+![alt text](link "hover text")
+
+and
+
+[![Build Status](https://travis-ci.org/mattermost/platform.svg?branch=master)](https://travis-ci.org/mattermost/platform) [![Github](https://assets-cdn.github.com/favicon.ico)](https://github.com/mattermost/platform)
+```
+Renders as:
+
+![alt text](link "hover text")
+
+and
+
+[![Build Status](https://travis-ci.org/mattermost/platform.svg?branch=master)](https://travis-ci.org/mattermost/platform) [![Github](https://assets-cdn.github.com/favicon.ico)](https://github.com/mattermost/platform)
+
+## Emojis
+
+Check out a full list of emojis [here](http://www.emoji-cheat-sheet.com/).
+
+```
+:smile: :+1: :sheep:
+```
+Renders as:
+:smile: :+1: :sheep:
+
+## Lines:
+
+Create a line by using three `*`, `_`, or `-`.
+
+`***` renders as:
+***
+
+## Block quotes:
+
+Create block quotes using `>`.
+
+`> block quotes` renders as:
+> block quotes
+
+## Lists:
+
+Create a list by using `*` or `-` as bullets. Indent a bullet point by adding two spaces in front of it.
+```
+* list item one
+* list item two
+ * item two sub-point
+```
+Renders as:
+* list item one
+* list item two
+ * item two sub-point
+
+Make it an ordered list by using numbers instead:
+```
+1. Item one
+2. Item two
+```
+Renders as:
+1. Item one
+2. Item two
+
+## Tables:
+
+Create a table by placing a dashed line under the header row and separating the columns with a pipe `|`. (The columns don’t need to line up exactly for it to work). Choose how to align table columns by including colons `:` within the header row.
+```
+| Left-Aligned | Center Aligned | Right Aligned |
+| :------------ |:---------------:| -----:|
+| Left column 1 | this text | $100 |
+| Left column 2 | is | $10 |
+| Left column 3 | centered | $1 |
+```
+
+Renders as:
+
+| Left-Aligned | Center Aligned | Right Aligned |
+| :------------ |:---------------:| -----:|
+| Left column 1 | this text | $100 |
+| Left column 2 | is | $10 |
+| Left column 3 | centered | $1 |
+
+## Headings:
+
+Make a heading by typing # and a space before your title. For smaller headings, use more #’s.
+```
+# Large heading
+## Smaller heading
+### Even smaller heading
+```
+Renders as:
+# Large Heading
+## Smaller Heading
+### Even smaller heading
+
+Alternatively, for the large heading you can underline the text using `===`. For the smaller heading you can underline using `---`
+```
+Large Heading
+=============
+
+Smaller Heading
+--------------
+```
+Renders as:
+Large Heading
+=============
+
+Smaller Heading
+--------------
diff --git a/doc/help/README.md b/doc/help/README.md
new file mode 100644
index 000000000..3b3c1709b
--- /dev/null
+++ b/doc/help/README.md
@@ -0,0 +1,18 @@
+## Help
+
+- Getting Started
+ - [Sign-in](Sign-in.md)
+
+- User Interface
+ - Main Menu
+ - [Team Settings ](https://github.com/mattermost/platform/blob/help-docs-update/doc/help/Team-Settings.md)
+ - [General Settings](https://github.com/mattermost/platform/blob/help-docs-update/doc/help/Team-Settings.md#general)
+ - [Slack Import](https://github.com/mattermost/platform/blob/help-docs-update/doc/help/Team-Settings.md#import-from-slack-beta)
+ - [Manage Members](Manage-Members.md)
+ - Messaging
+ - [Mattermost Markdown Formatting](help/Markdown.md)
+ - [Search](Search.md)
+
+- System Console
+ - Team
+ - [Team Statistics](system-console/Team-Statistics.md)
diff --git a/doc/process/overview.md b/doc/process/overview.md
index ca15de3a7..8109163d9 100644
--- a/doc/process/overview.md
+++ b/doc/process/overview.md
@@ -136,8 +136,6 @@ Examples:
- [Do not clear LastActivityAt for GetProfiles #1396](https://github.com/mattermost/platform/pull/1396/files)
- [Update to proxy_pass #1331](https://github.com/mattermost/platform/pull/1331)
-In general, it's
-
## Release
Mattermost ships stable releases on the 16th of the month. Releases begin with a planning process reviewing internal designs and community feedback in the context of the product purpose. Feature development is done in weekly sprints, and releases end with feature complete, stablization, code complete and release candidate milestones prior to final release.
diff --git a/doc/usage/Markdown.md b/doc/usage/Markdown.md
index dd90ede19..65e6f2121 100644
--- a/doc/usage/Markdown.md
+++ b/doc/usage/Markdown.md
@@ -1,180 +1,3 @@
# Markdown Help
-Markdown makes it easy to format messages. Type a message as you normally would, and use these rules to render it with special formatting.
-
-## Text Style:
-
-You can use either `_` or `*` around a word to make it italic. Use two to make it bold.
-
-* `_italics_` renders as _italics_
-* `**bold**` renders as **bold**
-* `**_bold-italic_**` renders as **_bold-italics_**
-* `~~strikethrough~~` renders as ~~strikethrough~~
-
-## Code Block:
-
-Create a code block by indenting each line by four spaces, or by placing ``` on the line above and below your code.
-
-Example:
-
- ```
- code block
- ```
-
-Renders as:
-```
-code block
-```
-
-### Syntax Highlighting
-
-To add syntax highlighting, type the language to be highlighted after the ``` at the beginning of the code block.
-
-Supported languages are:
-`diff, apache, makefile, http, json, markdown, javascript, css, nginx, objectivec, python, xml, perl, bash, php, coffee (CoffeeScript), cs (C#), cpp (C++), sql, go, ruby, java, ini, latex`
-
-Example:
-
- ``` go
- package main
- import "fmt"
- func main() {
- fmt.Println("Hello, 世界")
- }
- ```
-
-Renders as:
-``` go
-package main
-import "fmt"
-func main() {
- fmt.Println("Hello, 世界")
-}
-```
-
-## In-line Code:
-
-Create in-line monospaced font by surrounding it with backticks.
-```
-`monospace`
-```
-Renders as: `monospace`.
-
-## Links:
-
-Create labeled links by putting the desired text in square brackets and the associated link in normal brackets.
-
-`[Check out Mattermost!](www.mattermost.com)`
-
-Renders as: [Check out Mattermost!](www.mattermost.com)
-
-## In-line Images
-
-Create in-line images using an `!` followed by the alt text in square brackets and the link in normal brackets. Add hover text by placing it in quotes after the link.
-```
-![alt text](link "hover text")
-
-and
-
-[![Build Status](https://travis-ci.org/mattermost/platform.svg?branch=master)](https://travis-ci.org/mattermost/platform) [![Github](https://assets-cdn.github.com/favicon.ico)](https://github.com/mattermost/platform)
-```
-Renders as:
-
-![alt text](link "hover text")
-
-and
-
-[![Build Status](https://travis-ci.org/mattermost/platform.svg?branch=master)](https://travis-ci.org/mattermost/platform) [![Github](https://assets-cdn.github.com/favicon.ico)](https://github.com/mattermost/platform)
-
-## Emojis
-
-Check out a full list of emojis [here](http://www.emoji-cheat-sheet.com/).
-
-```
-:smile: :+1: :sheep:
-```
-Renders as:
-:smile: :+1: :sheep:
-
-## Lines:
-
-Create a line by using three `*`, `_`, or `-`.
-
-`***` renders as:
-***
-
-## Block quotes:
-
-Create block quotes using `>`.
-
-`> block quotes` renders as:
-> block quotes
-
-## Lists:
-
-Create a list by using `*` or `-` as bullets. Indent a bullet point by adding two spaces in front of it.
-```
-* list item one
-* list item two
- * item two sub-point
-```
-Renders as:
-* list item one
-* list item two
- * item two sub-point
-
-Make it an ordered list by using numbers instead:
-```
-1. Item one
-2. Item two
-```
-Renders as:
-1. Item one
-2. Item two
-
-## Tables:
-
-Create a table by placing a dashed line under the header row and separating the columns with a pipe `|`. (The columns don’t need to line up exactly for it to work). Choose how to align table columns by including colons `:` within the header row.
-```
-| Left-Aligned  | Center Aligned  | Right Aligned |
-| :------------ |:---------------:| -----:|
-| Left column 1 | this text       |  $100 |
-| Left column 2 | is              |   $10 |
-| Left column 3 | centered        |    $1 |
-```
-
-Renders as:
-
-| Left-Aligned  | Center Aligned  | Right Aligned |
-| :------------ |:---------------:| -----:|
-| Left column 1 | this text       |  $100 |
-| Left column 2 | is              |   $10 |
-| Left column 3 | centered        |    $1 |
-
-## Headings:
-
-Make a heading by typing # and a space before your title. For smaller headings, use more #’s.
-```
-# Large heading
-## Smaller heading
-### Even smaller heading
-```
-Renders as:
-# Large Heading
-## Smaller Heading
-### Even smaller heading
-
-Alternatively, for the large heading you can underline the text using `===`. For the smaller heading you can underline using `---`
-```
-Large Heading
-=============
-
-Smaller Heading
---------------
-```
-Renders as:
-Large Heading
-=============
-
-Smaller Heading
---------------
+Moved to [help/Markdown.md](../help/Markdown.md)
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx
index 6e12c7c14..08c4a48ea 100644
--- a/web/react/components/channel_header.jsx
+++ b/web/react/components/channel_header.jsx
@@ -101,9 +101,11 @@ export default class ChannelHeader extends React.Component {
let terms = '';
if (user.notify_props && user.notify_props.mention_keys) {
const termKeys = UserStore.getCurrentMentionKeys();
- if (user.notify_props.all === 'true' && termKeys.indexOf('@all') !== -1) {
- termKeys.splice(termKeys.indexOf('@all'), 1);
- }
+
+ // if (user.notify_props.all === 'true' && termKeys.indexOf('@all') !== -1) {
+ // termKeys.splice(termKeys.indexOf('@all'), 1);
+ // }
+
if (user.notify_props.channel === 'true' && termKeys.indexOf('@channel') !== -1) {
termKeys.splice(termKeys.indexOf('@channel'), 1);
}
diff --git a/web/react/components/command_list.jsx b/web/react/components/command_list.jsx
index ff83d0420..7fc0f79cf 100644
--- a/web/react/components/command_list.jsx
+++ b/web/react/components/command_list.jsx
@@ -81,7 +81,7 @@ export default class CommandList extends React.Component {
<div
ref='mentionlist'
className='command-box'
- style={{height: (this.state.suggestions.length * 56) + 2}}
+ style={{height: (suggestions.length * 56) + 2}}
>
{suggestions}
</div>
diff --git a/web/react/components/member_list_team.jsx b/web/react/components/member_list_team.jsx
index 72fdb7be9..f1c31131f 100644
--- a/web/react/components/member_list_team.jsx
+++ b/web/react/components/member_list_team.jsx
@@ -2,17 +2,56 @@
// See License.txt for license information.
import MemberListTeamItem from './member_list_team_item.jsx';
+import UserStore from '../stores/user_store.jsx';
export default class MemberListTeam extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.getUsers = this.getUsers.bind(this);
+ this.onChange = this.onChange.bind(this);
+
+ this.state = {
+ users: this.getUsers()
+ };
+ }
+
+ componentDidMount() {
+ UserStore.addChangeListener(this.onChange);
+ }
+
+ componentWillUnmount() {
+ UserStore.removeChangeListener(this.onChange);
+ }
+
+ getUsers() {
+ const profiles = UserStore.getProfiles();
+ const users = [];
+
+ for (const id of Object.keys(profiles)) {
+ users.push(profiles[id]);
+ }
+
+ users.sort((a, b) => a.username.localeCompare(b.username));
+
+ return users;
+ }
+
+ onChange() {
+ this.setState({
+ users: this.getUsers()
+ });
+ }
+
render() {
- const memberList = this.props.users.map(function makeListItem(user) {
+ const memberList = this.state.users.map((user) => {
return (
<MemberListTeamItem
key={user.id}
user={user}
/>
);
- }, this);
+ });
return (
<table className='table more-table member-list-holder'>
@@ -23,7 +62,3 @@ export default class MemberListTeam extends React.Component {
);
}
}
-
-MemberListTeam.propTypes = {
- users: React.PropTypes.arrayOf(React.PropTypes.object).isRequired
-};
diff --git a/web/react/components/mention_list.jsx b/web/react/components/mention_list.jsx
index d1b27cf99..297d5c719 100644
--- a/web/react/components/mention_list.jsx
+++ b/web/react/components/mention_list.jsx
@@ -183,12 +183,12 @@ export default class MentionList extends React.Component {
}
}
- var all = {};
- all.username = 'all';
- all.nickname = '';
- all.secondary_text = 'Notifies everyone in the team';
- all.id = 'allmention';
- users.push(all);
+ // var all = {};
+ // all.username = 'all';
+ // all.nickname = '';
+ // all.secondary_text = 'Notifies everyone in the team';
+ // all.id = 'allmention';
+ // users.push(all);
var channel = {};
channel.username = 'channel';
diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx
index a14434bfc..c286ee6f9 100644
--- a/web/react/components/navbar_dropdown.jsx
+++ b/web/react/components/navbar_dropdown.jsx
@@ -8,6 +8,8 @@ import TeamStore from '../stores/team_store.jsx';
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import AboutBuildModal from './about_build_modal.jsx';
+import TeamMembersModal from './team_members_modal.jsx';
+import ToggleModalButton from './toggle_modal_button.jsx';
import UserSettingsModal from './user_settings/user_settings_modal.jsx';
import Constants from '../utils/constants.jsx';
@@ -117,13 +119,9 @@ export default class NavbarDropdown extends React.Component {
if (isAdmin) {
manageLink = (
<li>
- <a
- href='#'
- data-toggle='modal'
- data-target='#team_members'
- >
+ <ToggleModalButton dialogType={TeamMembersModal}>
{'Manage Members'}
- </a>
+ </ToggleModalButton>
</li>
);
diff --git a/web/react/components/rhs_root_post.jsx b/web/react/components/rhs_root_post.jsx
index 8142888ba..3d3d9e13f 100644
--- a/web/react/components/rhs_root_post.jsx
+++ b/web/react/components/rhs_root_post.jsx
@@ -38,7 +38,9 @@ export default class RhsRootPost extends React.Component {
}
render() {
var post = this.props.post;
- var isOwner = UserStore.getCurrentId() === post.user_id;
+ var currentUser = UserStore.getCurrentUser();
+ var isOwner = currentUser.id === post.user_id;
+ var isAdmin = utils.isAdmin(currentUser.roles);
var timestamp = UserStore.getProfile(post.user_id).update_at;
var channel = ChannelStore.get(post.channel_id);
@@ -61,11 +63,54 @@ export default class RhsRootPost extends React.Component {
}
}
- var ownerOptions;
+ var dropdownContents = [];
+
if (isOwner) {
- ownerOptions = (
- <div>
- <a href='#'
+ dropdownContents.push(
+ <li
+ key='rhs-root-edit'
+ role='presentation'
+ >
+ <a
+ href='#'
+ role='menuitem'
+ data-toggle='modal'
+ data-target='#edit_post'
+ data-refocusid='#reply_textbox'
+ data-title={type}
+ data-message={post.message}
+ data-postid={post.id}
+ data-channelid={post.channel_id}
+ >
+ {'Edit'}
+ </a>
+ </li>
+ );
+ }
+
+ if (isOwner || isAdmin) {
+ dropdownContents.push(
+ <li
+ key='rhs-root-delete'
+ role='presentation'
+ >
+ <a
+ href='#'
+ role='menuitem'
+ onClick={() => EventHelpers.showDeletePostModal(post, this.props.commentCount)}
+ >
+ {'Delete'}
+ </a>
+ </li>
+ );
+ }
+
+ var rootOptions = '';
+ if (dropdownContents.length > 0) {
+ rootOptions = (
+ <div className='dropdown'>
+ <a
+ href='#'
className='post__dropdown dropdown-toggle'
type='button'
data-toggle='dropdown'
@@ -75,30 +120,7 @@ export default class RhsRootPost extends React.Component {
className='dropdown-menu'
role='menu'
>
- <li role='presentation'>
- <a
- href='#'
- role='menuitem'
- data-toggle='modal'
- data-target='#edit_post'
- data-refocusid='#reply_textbox'
- data-title={type}
- data-message={post.message}
- data-postid={post.id}
- data-channelid={post.channel_id}
- >
- {'Edit'}
- </a>
- </li>
- <li role='presentation'>
- <a
- href='#'
- role='menuitem'
- onClick={() => EventHelpers.showDeletePostModal(post, this.props.commentCount)}
- >
- {'Delete'}
- </a>
- </li>
+ {dropdownContents}
</ul>
</div>
);
@@ -166,7 +188,7 @@ export default class RhsRootPost extends React.Component {
</li>
<li className='col col__reply'>
<div className='dropdown'>
- {ownerOptions}
+ {rootOptions}
</div>
</li>
</ul>
diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx
index 60e8b3856..108b1c1b9 100644
--- a/web/react/components/sidebar_right_menu.jsx
+++ b/web/react/components/sidebar_right_menu.jsx
@@ -1,6 +1,8 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+import TeamMembersModal from './team_members_modal.jsx';
+import ToggleModalButton from './toggle_modal_button.jsx';
import UserSettingsModal from './user_settings/user_settings_modal.jsx';
import UserStore from '../stores/user_store.jsx';
import * as client from '../utils/client.jsx';
@@ -78,12 +80,9 @@ export default class SidebarRightMenu extends React.Component {
);
manageLink = (
<li>
- <a
- href='#'
- data-toggle='modal'
- data-target='#team_members'
- >
- <i className='fa fa-users'></i>Manage Members</a>
+ <ToggleModalButton dialogType={TeamMembersModal}>
+ <i className='fa fa-users'></i>{'Manage Members'}
+ </ToggleModalButton>
</li>
);
}
diff --git a/web/react/components/team_members.jsx b/web/react/components/team_members.jsx
deleted file mode 100644
index cd0766012..000000000
--- a/web/react/components/team_members.jsx
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import UserStore from '../stores/user_store.jsx';
-import MemberListTeam from './member_list_team.jsx';
-import * as utils from '../utils/utils.jsx';
-
-function getStateFromStores() {
- var users = UserStore.getProfiles();
- var memberList = [];
- for (var id in users) {
- if (users.hasOwnProperty(id)) {
- memberList.push(users[id]);
- }
- }
-
- memberList.sort(function sort(a, b) {
- if (a.username < b.username) {
- return -1;
- }
-
- if (a.username > b.username) {
- return 1;
- }
-
- return 0;
- });
-
- return {
- member_list: memberList
- };
-}
-
-export default class TeamMembers extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChange = this.onChange.bind(this);
-
- this.state = getStateFromStores();
- }
-
- componentDidMount() {
- UserStore.addChangeListener(this.onChange);
-
- var self = this;
- $(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function show() {
- self.setState({render_members: false});
- });
-
- $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', function hide() {
- self.setState({render_members: true});
- });
- }
-
- componentWillUnmount() {
- UserStore.removeChangeListener(this.onChange);
- }
-
- onChange() {
- var newState = getStateFromStores();
- if (!utils.areObjectsEqual(newState, this.state)) {
- this.setState(newState);
- }
- }
-
- render() {
- var serverError = null;
-
- if (this.state.server_error) {
- serverError = <label className='has-error control-label'>{this.state.server_error}</label>;
- }
-
- var renderMembers = '';
-
- if (this.state.render_members) {
- renderMembers = <MemberListTeam users={this.state.member_list} />;
- }
-
- return (
- <div
- className='modal fade more-modal'
- ref='modal'
- id='team_members'
- tabIndex='-1'
- role='dialog'
- aria-hidden='true'
- >
- <div className='modal-dialog'>
- <div className='modal-content'>
- <div className='modal-header'>
- <button
- type='button'
- className='close'
- data-dismiss='modal'
- aria-label='Close'
- >
- <span aria-hidden='true'>×</span>
- </button>
- <h4
- className='modal-title'
- id='myModalLabel'
- >{this.props.teamDisplayName + ' Members'}</h4>
- </div>
- <div
- ref='modalBody'
- className='modal-body'
- >
- <div className='team-member-list'>
- {renderMembers}
- </div>
- {serverError}
- </div>
- <div className='modal-footer'>
- <button
- type='button'
- className='btn btn-default'
- data-dismiss='modal'
- >Close</button>
- </div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-TeamMembers.propTypes = {
- teamDisplayName: React.PropTypes.string
-};
diff --git a/web/react/components/team_members_modal.jsx b/web/react/components/team_members_modal.jsx
new file mode 100644
index 000000000..0a30a2202
--- /dev/null
+++ b/web/react/components/team_members_modal.jsx
@@ -0,0 +1,69 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import MemberListTeam from './member_list_team.jsx';
+import TeamStore from '../stores/team_store.jsx';
+
+const Modal = ReactBootstrap.Modal;
+
+export default class TeamMembersModal extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.onShow = this.onShow.bind(this);
+ }
+
+ componentDidMount() {
+ if (this.props.show) {
+ this.onShow();
+ }
+ }
+
+ componentDidUpdate(prevProps) {
+ if (this.props.show && !prevProps.show) {
+ this.onShow();
+ }
+ }
+
+ onShow() {
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 300);
+ if ($(window).width() > 768) {
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
+ }
+ }
+
+ render() {
+ const team = TeamStore.getCurrent();
+
+ return (
+ <Modal
+ dialogClassName='team-members-modal'
+ show={this.props.show}
+ onHide={this.props.onHide}
+ >
+ <Modal.Header closeButton={true}>
+ {team.display_name + ' Members'}
+ </Modal.Header>
+ <Modal.Body ref='modalBody'>
+ <div className='team-member-list'>
+ <MemberListTeam />
+ </div>
+ </Modal.Body>
+ <Modal.Footer>
+ <button
+ type='button'
+ className='btn btn-default'
+ onClick={this.props.onHide}
+ >
+ {'Close'}
+ </button>
+ </Modal.Footer>
+ </Modal>
+ );
+ }
+}
+
+TeamMembersModal.propTypes = {
+ show: React.PropTypes.bool.isRequired,
+ onHide: React.PropTypes.func.isRequired
+};
diff --git a/web/react/components/user_settings/user_settings_general.jsx b/web/react/components/user_settings/user_settings_general.jsx
index b3ec7ddd7..962efd7a2 100644
--- a/web/react/components/user_settings/user_settings_general.jsx
+++ b/web/react/components/user_settings/user_settings_general.jsx
@@ -438,12 +438,12 @@ export default class UserSettingsGeneralTab extends React.Component {
if (this.props.activeSection === 'email') {
const emailEnabled = global.window.mm_config.SendEmailNotifications === 'true';
const emailVerificationEnabled = global.window.mm_config.RequireEmailVerification === 'true';
- let helpText = 'Email is used for notifications, and requires verification if changed.';
+ let helpText = 'Email is used for sign-in, notifications, and password reset. Email requires verification if changed.';
if (!emailEnabled) {
helpText = <div className='setting-list__hint text-danger'>{'Email has been disabled by your system administrator. No notification emails will be sent until it is enabled.'}</div>;
} else if (!emailVerificationEnabled) {
- helpText = 'Email is used for notifications.';
+ helpText = 'Email is used for sign-in, notifications, and password reset.';
} else if (this.state.emailChangeInProgress) {
const newEmail = UserStore.getCurrentUser().email;
if (newEmail) {
diff --git a/web/react/components/user_settings/user_settings_notifications.jsx b/web/react/components/user_settings/user_settings_notifications.jsx
index e36aed395..e025bf670 100644
--- a/web/react/components/user_settings/user_settings_notifications.jsx
+++ b/web/react/components/user_settings/user_settings_notifications.jsx
@@ -512,7 +512,7 @@ export default class NotificationsTab extends React.Component {
}.bind(this);
inputs.push(
<div key='userNotificationAllOption'>
- <div className='checkbox'>
+ <div className='checkbox hidden'>
<label>
<input
type='checkbox'
@@ -590,9 +590,11 @@ export default class NotificationsTab extends React.Component {
if (this.state.mentionKey) {
keys.push('@' + user.username);
}
- if (this.state.allKey) {
- keys.push('@all');
- }
+
+ // if (this.state.allKey) {
+ // keys.push('@all');
+ // }
+
if (this.state.channelKey) {
keys.push('@channel');
}
diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx
index 161e6ab22..b73dfdafe 100644
--- a/web/react/pages/channel.jsx
+++ b/web/react/pages/channel.jsx
@@ -14,7 +14,6 @@ import DeletePostModal from '../components/delete_post_modal.jsx';
import MoreChannelsModal from '../components/more_channels.jsx';
import PostDeletedModal from '../components/post_deleted_modal.jsx';
import TeamSettingsModal from '../components/team_settings_modal.jsx';
-import TeamMembersModal from '../components/team_members.jsx';
import RemovedFromChannelModal from '../components/removed_from_channel_modal.jsx';
import RegisterAppModal from '../components/register_app_modal.jsx';
import ImportThemeModal from '../components/user_settings/import_theme_modal.jsx';
@@ -87,11 +86,6 @@ function setupChannelPage(props, team, channel) {
);
ReactDOM.render(
- <TeamMembersModal teamDisplayName={props.TeamDisplayName} />,
- document.getElementById('team_members_modal')
- );
-
- ReactDOM.render(
<RenameChannelModal />,
document.getElementById('rename_channel_modal')
);
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index 6281813e9..372e15556 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -68,7 +68,8 @@ export default {
TYPING: 'typing'
},
- SPECIAL_MENTIONS: ['all', 'channel'],
+ //SPECIAL_MENTIONS: ['all', 'channel'],
+ SPECIAL_MENTIONS: ['channel'],
CHARACTER_LIMIT: 4000,
IMAGE_TYPES: ['jpg', 'gif', 'bmp', 'png', 'jpeg'],
AUDIO_TYPES: ['mp3', 'wav', 'wma', 'm4a', 'flac', 'aac'],