Merge remote-tracking branch 'upstream/master' into PLT-637
Conflicts: web/react/utils/constants.jsx
Этот коммит содержится в:
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,10 +1,14 @@
|
||||
# Mattermost Changelog
|
||||
|
||||
## UNDER DEVELOPMENT - Release v1.1.0
|
||||
## UNDER DEVELOPMENT Release v1.2.0
|
||||
|
||||
The "UNDER DEVELOPMENT" section of the Mattermost changelog appears in the product's `master` branch to note key changes committed to master and are on their way to the next stable release. When a stable release is pushed the "UNDER DEVELOPMENT" heading is removed from the final changelog of the release.
|
||||
|
||||
- **Final release anticipated:** 2015-10-16
|
||||
- **Final release anticipated:** 2015-11-16
|
||||
|
||||
## Release v1.1.0
|
||||
|
||||
Released: 2015-10-16
|
||||
|
||||
### Release Highlights
|
||||
|
||||
@@ -44,6 +48,11 @@ Messaging and Notifications
|
||||
- Fixed bug where System Administrator did not have Team Administrator permissions
|
||||
- Fixed bug causing scrolling to jump when the right hand sidebar opened and closed
|
||||
|
||||
### Known Issues
|
||||
|
||||
- Slack import is unstable due to change in Slack export format
|
||||
- Uploading a .flac file breaks the file previewer on iOS
|
||||
|
||||
### Contributors
|
||||
|
||||
Many thanks to our external contributors. In no particular order:
|
||||
|
||||
18
Makefile
18
Makefile
@@ -106,11 +106,12 @@ travis:
|
||||
mv $(DIST_PATH)/web/static/js/bundle.min.js $(DIST_PATH)/web/static/js/bundle-$(BUILD_NUMBER).min.js
|
||||
mv $(DIST_PATH)/web/static/js/libs.min.js $(DIST_PATH)/web/static/js/libs-$(BUILD_NUMBER).min.js
|
||||
|
||||
@sed -i'.bak' 's|react-with-addons-0.13.3.js|react-with-addons-0.13.3.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|jquery-1.11.1.js|jquery-1.11.1.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|react-0.14.0.js|react-0.14.0.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|react-dom-0.14.0.js|react-dom-0.14.0.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|jquery-2.1.4.js|jquery-2.1.4.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|bootstrap-3.3.5.js|bootstrap-3.3.5.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|react-bootstrap-0.25.1.js|react-bootstrap-0.25.1.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|perfect-scrollbar.js|perfect-scrollbar.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|react-bootstrap-0.27.1.js|react-bootstrap-0.27.1.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|perfect-scrollbar-0.6.7.jquery.js|perfect-scrollbar-0.6.7.jquery.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|bundle.js|bundle-$(BUILD_NUMBER).min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|libs.min.js|libs-$(BUILD_NUMBER).min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
rm $(DIST_PATH)/web/templates/*.bak
|
||||
@@ -290,11 +291,12 @@ dist: install
|
||||
mv $(DIST_PATH)/web/static/js/bundle.min.js $(DIST_PATH)/web/static/js/bundle-$(BUILD_NUMBER).min.js
|
||||
mv $(DIST_PATH)/web/static/js/libs.min.js $(DIST_PATH)/web/static/js/libs-$(BUILD_NUMBER).min.js
|
||||
|
||||
@sed -i'.bak' 's|react-with-addons-0.13.3.js|react-with-addons-0.13.3.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|jquery-1.11.1.js|jquery-1.11.1.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|react-0.14.0.js|react-0.14.0.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|react-dom-0.14.0.js|react-dom-0.14.0.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|jquery-2.1.4.js|jquery-2.1.4.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|bootstrap-3.3.5.js|bootstrap-3.3.5.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|react-bootstrap-0.25.1.js|react-bootstrap-0.25.1.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|perfect-scrollbar.js|perfect-scrollbar.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|react-bootstrap-0.27.1.js|react-bootstrap-0.27.1.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|perfect-scrollbar-0.6.7.jquery.js|perfect-scrollbar-0.6.7.jquery.min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|bundle.js|bundle-$(BUILD_NUMBER).min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
@sed -i'.bak' 's|libs.min.js|libs-$(BUILD_NUMBER).min.js|g' $(DIST_PATH)/web/templates/head.html
|
||||
rm $(DIST_PATH)/web/templates/*.bak
|
||||
|
||||
@@ -19,6 +19,8 @@ func Setup() {
|
||||
StartServer()
|
||||
InitApi()
|
||||
Client = model.NewClient("http://localhost" + utils.Cfg.ServiceSettings.ListenAddress)
|
||||
|
||||
Srv.Store.MarkSystemRanUnitTests()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,15 +8,17 @@ Get up and running quickly with Docker-based install
|
||||
- [AWS Elastic Beanstalk Setup](install/Amazon-Elastic-Beanstalk.md)
|
||||
- [Docker Single Container Preview Setup](install/Docker-Single-Container.md)
|
||||
- [SMTP Email Setup](install/SMTP-Email-Setup.md)
|
||||
- [System Console Settings](install/Configuration-Settings.md)
|
||||
|
||||
#### Production Installation
|
||||
Set up Mattermost in your data center
|
||||
- [Software and Hardware Requirements](install/Requirements.md)
|
||||
- [Production Ubuntu Setup](install/Production-Ubuntu.md)
|
||||
- [SMTP Email Setup](install/SMTP-Email-Setup.md)
|
||||
- [System Console Settings](install/Configuration-Settings.md)
|
||||
|
||||
#### Configuration and Management
|
||||
- Configuration Settings Overview
|
||||
- [System Console Settings](install/Configuration-Settings.md)
|
||||
- [GitLab SSO Configuration](integrations/Single-Sign-On/Gitlab.md)
|
||||
- [Mattermost Release Numbering Scheme](install/Release-Numbering.md)
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ Any issues? Please let us know on our forums at: http://forum.mattermost.org
|
||||
3. Reload your bashrc
|
||||
`source ~/.bashrc`
|
||||
6. Install Node.js
|
||||
1. Download the newest version of the Node.js sources from https://nodejs.org/download/
|
||||
1. Download the newest version of the Node.js sources from https://nodejs.org/en/download/
|
||||
2. Extract the contents of the package and cd into the extracted files
|
||||
3. Compile and install Node.js
|
||||
`./configure`
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
### Slack Import (Beta)
|
||||
### Slack Import
|
||||
|
||||
*Note: As a SaaS service, Slack is able to change its export format quickly. If you encounter issues not mentioned in the documentation below, please let us know by [filing an issue](https://github.com/mattermost/platform/issues).*
|
||||
*Note: As a proprietary SaaS service, Slack is able to change its export format quickly and without notice. If you encounter issues not mentioned in the documentation below, please alert the product team by [filing an issue](https://github.com/mattermost/platform/issues).*
|
||||
|
||||
#### Usage
|
||||
|
||||
The Slack Import feature in Mattermost is in "Beta" and focus is on supporting migration of teams of less than 100 registered users. The feature can be accessed from by Team Administrators and Team Owners via the `Team Settings -> Import` menu option.
|
||||
The Slack Import feature in Mattermost is in "Beta" and focus is on supporting migration of teams of less than 100 registered users. To use:
|
||||
|
||||
- **It is highly recommended that you test Slack import before applying it to an instance intended for production.** If you use Docker, you can spin up a test instance in one line (`docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform`). If you don't use Docker, there are [step-by-step instructions to install Mattermost in preview mode in less than 5 minutes](../install/Docker-Single-Container.md).
|
||||
1. Generate a Slack "Export" file from **Slack > Team Settings > Import/Export Data > Export > Start Export**
|
||||
|
||||
Mattermost currently supports the processing of an "Export" file from Slack containing account information and public channel archives from a Slack team.
|
||||
2. In Mattermost go to **Team Settings > Import > Import from Slack**. _Team Owner_ or _Team Administrator_ role is required to access this menu option.
|
||||
|
||||
- Emails and usernames from Slack are used to create new Mattermost accounts, connected to messages history in imported Slack channels. Users can activate these accounts and by going to the Password Reset screen in Mattermost to set new credentials.
|
||||
- Once logged in, users will have access to previous Slack messages shared in public channels, now imported to Mattermost.
|
||||
3. Click **Select file** to upload Slack export file and click **Import**.
|
||||
|
||||
#### Limitations:
|
||||
4. Emails and usernames from Slack are used to create new Mattermost accounts
|
||||
|
||||
5. Slack users can activate their new Mattermost accounts by using Mattermost's Password Reset screen with their email addresses from Slack to set new passwords for their Mattermost accounts
|
||||
|
||||
6. Once logged in, the Mattermost users will have access to previous Slack messages in the public channels imported from Slack.
|
||||
|
||||
**It is highly recommended that you test Slack import before applying it to an instance intended for production.** If you use Docker, you can spin up a test instance in one line (`docker run --name mattermost-dev -d --publish 8065:80 mattermost/platform`). If you don't use Docker, there are [step-by-step instructions](../install/Docker-Single-Container.md) to install Mattermost in preview mode in less than 5 minutes.
|
||||
|
||||
#### Notes:
|
||||
|
||||
- Newly added markdown suppport in Slack's Posts 2.0 feature announced on September 28, 2015 is not yet supported.
|
||||
- Slack does not export files or images your team has stored in Slack's database. Mattermost will provide links to the location of your assets in Slack's web UI.
|
||||
- Slack does not export any content from private groups or direct messages that your team has stored in Slack's database.
|
||||
- The Beta release of Slack Import does not offer pre-checks or roll-back and will not import Slack accounts with username or email address collisions with existing Mattermost accounts. Also, Slack channel names with underscores will not import. Also, mentions do not yet resolve as Mattermost usernames (still shows Slack ID).
|
||||
- In Beta, Slack accounts with username or email address collisions with existing Mattermost accounts will not import and mentions do not resolve as Mattermost usernames (still shows Slack ID). No pre-check or roll-back is currently offered.
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
## Set up Mattermost Server
|
||||
1. For the purposes of this guide we will assume this server has an IP address of 10.10.10.2
|
||||
1. Download the latest Mattermost Server by typing:
|
||||
* ``` wget https://github.com/mattermost/platform/releases/download/v1.0.0/mattermost.tar.gz```
|
||||
* ``` wget https://github.com/mattermost/platform/releases/download/v1.1.0/mattermost.tar.gz```
|
||||
1. Unzip the Mattermost Server by typing:
|
||||
* ``` tar -xvzf mattermost.tar.gz```
|
||||
1. For the sake of making this guide simple we located the files at `/home/ubuntu/mattermost`. In the future we will give guidance for storing under `/opt`.
|
||||
|
||||
@@ -1,50 +1,46 @@
|
||||
|
||||
## SMTP Email Setup
|
||||
|
||||
In some product evaluation setups email is intentionally bypassed using a `ByPassEmail=true` option. This option allows account creation and system operation without having to set up an email service (e.g. no email verification is required for account creation). This also means neither email notifications nor password reset by email are available.
|
||||
In product evaluation setups with single-container Docker instances, email is intentionally disabled. This allows account creation and system operation without having to set up email, but it also means email notification and password reset functionality aren't available.
|
||||
|
||||
To enable email, turn this option off by setting `ByPassEmail=false` and configuring an SMTP email service as follows:
|
||||
### How to enable email
|
||||
|
||||
1. **Set up an SMTP email sending service.** (If you already have credentials for a SMTP server you can skip this step.)
|
||||
1. [Setup Amazon Simple Email Service](https://console.aws.amazon.com/ses)
|
||||
2. From the `SMTP Settings` menu click `Create My SMTP Credentials`
|
||||
3. Copy the `Server Name`, `Port`, `SMTP Username`, and `SMTP Password`
|
||||
4. From the `Domains` menu setup and verify a new domain. It it also a good practice to enable `Generate DKIM Settings` for this domain.
|
||||
5. Choose an email address like `feedback@example.com` for Mattermost to send emails from.
|
||||
6. Test sending an email from `feedback@example.com` by clicking the `Send a Test Email` button and verify everything appears to be working correctly.
|
||||
2. **Modify the Mattermost configuration file config.json or config_docker.json with the SMTP information.**
|
||||
1. If you're running Mattermost on Amazon Beanstalk you can shell into the instance with the following commands
|
||||
2. `ssh ec2-user@[domain for the docker instance]`
|
||||
3. `sudo gpasswd -a ec2-user docker`
|
||||
4. Retrieve the name of the container with `sudo docker ps`
|
||||
5. `sudo docker exec -ti container_name /bin/bash`
|
||||
3. **Edit the config file `vi /config_docker.json` with the settings you captured from the step above.**
|
||||
1. See an example below and notice `ByPassEmail` has been set to `false`
|
||||
``` bash
|
||||
"EmailSettings": {
|
||||
"ByPassEmail" : false,
|
||||
"SMTPUsername": "AKIADTOVBGERKLCBV",
|
||||
"SMTPPassword": "jcuS8PuvcpGhpgHhlcpT1Mx42pnqMxQY",
|
||||
"SMTPServer": "email-smtp.us-east-1.amazonaws.com:465",
|
||||
"UseTLS": true,
|
||||
"FeedbackEmail": "feedback@example.com",
|
||||
"FeedbackName": "Feedback",
|
||||
"ApplePushServer": "",
|
||||
"ApplePushCertPublic": "",
|
||||
"ApplePushCertPrivate": ""
|
||||
}
|
||||
```
|
||||
4. **Restart Mattermost**
|
||||
1. Find the process id with `ps -A` and look for the process named `platform`
|
||||
2. Kill the process `kill pid`
|
||||
3. The service should restart automatically. Verify the Mattermost service is running with `ps -A`
|
||||
4. Current logged in users will not be affected, but upon logging out or session expiration users will be required to verify their email address.
|
||||
To enable email, configure an SMTP email service as follows:
|
||||
|
||||
1. **Set up an SMTP email sending service** (if you don't yet have an SMTP service with credentials)
|
||||
1. Any SMTP email service can be used, you just need the following information: `Server Name`, `Port`, `SMTP Username`, and `SMTP Password`.
|
||||
2. If you don't have an SMTP service, here are simple instructions to set one up with [Amazon Simple Email Service (SES)](https://aws.amazon.com/ses/):
|
||||
2. Go to [Amazon SES console](https://console.aws.amazon.com/ses) then `SMTP Settings > Create My SMTP Credentials`
|
||||
3. Copy the `Server Name`, `Port`, `SMTP Username`, and `SMTP Password` for Step 2 below.
|
||||
4. From the `Domains` menu set up and verify a new domain, then enable `Generate DKIM Settings` for the domain.
|
||||
5. Choose an sender address like `mattermost@example.com` and click `Send a Test Email` to verify setup is working correctly.
|
||||
2. **Configure SMTP settings**
|
||||
1. Open the **System Console** by logging into an existing team and accessing "System Console" from the main menu.
|
||||
1. Alternatively, if a team doesn't yet exist, go to `http://dockerhost:8065/` in your browser, create a team, then from the main menu click **System Console**
|
||||
2. Go to the **Email Settings** tab and configure the following:
|
||||
1. **Allow Sign Up With Email:** `true`
|
||||
2. **Send Email Notifications:** `true`
|
||||
3. **Require Email Verification:** `true`
|
||||
4. **Notification Display Name:** Display name on email account sending notifications
|
||||
5. **Notification Email Address:** Email address displayed on email account used to send notifications
|
||||
6. **SMTP Username**: `SMTP Username` from Step 1
|
||||
7. **SMTP Password**: `SMTP Password` from Step 1
|
||||
8. **SMTP Server**: `SMTP Server` from Step 1
|
||||
9. **SMTP Port**: `SMTP Port` from Step 1
|
||||
10. **Connection Security**: `TLS (Recommended)`
|
||||
11. Then click **Save**
|
||||
|
||||
3. **Restart Mattermost**
|
||||
1. Use `ps -A` to find the process ID ("pid") for service named `platform` and stop it using `kill [pid]`
|
||||
2. The service should restart automatically. Run `ps -A` to verify the `platform` is running again
|
||||
3. Use the reset password page (E.g. _example.com/teamname/reset_password_) to test that email is now working by entering your email and clicking **Reset my password**.
|
||||
4. Note: The next time users log out, or when their session tokens expire, each will be required to verify their email address.
|
||||
|
||||
### Troubleshooting SMTP
|
||||
|
||||
If you have issues with your SMTP install, from your Mattermost team site go to the main menu and open `System Console > Logs` to look for error messages related to your setup. You can do a search for the error code to narrow down the issue. Sometimes ISPs require nuanced setups for SMTP and error codes can hint at how to make the proper adjustments.
|
||||
If you have issues with your SMTP install, from your Mattermost team site go to the main menu and open **System Console -> Logs** to look for error messages related to your setup. You can do a search for the error code to narrow down the issue. Sometimes ISPs require nuanced setups for SMTP and error codes can hint at how to make the proper adjustments.
|
||||
|
||||
For example, if `System Console > Logs` has an error code reading:
|
||||
For example, if **System Console -> Logs** has an error code reading:
|
||||
|
||||
```
|
||||
Connection unsuccessful: Failed to add to email address - 554 5.7.1 <unknown[IP-ADDRESS]>: Client host rejected: Access denied
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
#### Important notes
|
||||
|
||||
1. **DO NOT manipulate the Mattermost database**
|
||||
- In particular, DO NOT delete data from the database, as this will most likely crash Mattermost in strange ways. Mattermost is designed to archive content continously and generally assumes data is never deleted.
|
||||
- In particular, DO NOT delete data from the database, as Mattermost is designed to stop working if data integrity has been compromised. The system is designed to archive content continously and generally assumes data is never deleted.
|
||||
|
||||
|
||||
@@ -84,16 +84,16 @@ func securityAndDiagnosticsJob() {
|
||||
if *utils.Cfg.ServiceSettings.EnableSecurityFixAlert {
|
||||
if result := <-api.Srv.Store.System().Get(); result.Err == nil {
|
||||
props := result.Data.(model.StringMap)
|
||||
lastSecurityTime, _ := strconv.ParseInt(props["LastSecurityTime"], 10, 0)
|
||||
lastSecurityTime, _ := strconv.ParseInt(props[model.SYSTEM_LAST_SECURITY_TIME], 10, 0)
|
||||
currentTime := model.GetMillis()
|
||||
|
||||
if (currentTime - lastSecurityTime) > 1000*60*60*24*1 {
|
||||
l4g.Debug("Checking for security update from Mattermost")
|
||||
|
||||
id := props["DiagnosticId"]
|
||||
id := props[model.SYSTEM_DIAGNOSTIC_ID]
|
||||
if len(id) == 0 {
|
||||
id = model.NewId()
|
||||
systemId := &model.System{Name: "DiagnosticId", Value: id}
|
||||
systemId := &model.System{Name: model.SYSTEM_DIAGNOSTIC_ID, Value: id}
|
||||
<-api.Srv.Store.System().Save(systemId)
|
||||
}
|
||||
|
||||
@@ -104,7 +104,13 @@ func securityAndDiagnosticsJob() {
|
||||
v.Set(utils.PROP_DIAGNOSTIC_OS, runtime.GOOS)
|
||||
v.Set(utils.PROP_DIAGNOSTIC_CATEGORY, utils.VAL_DIAGNOSTIC_CATEGORY_DEFAULT)
|
||||
|
||||
systemSecurityLastTime := &model.System{Name: "LastSecurityTime", Value: strconv.FormatInt(currentTime, 10)}
|
||||
if len(props[model.SYSTEM_RAN_UNIT_TESTS]) > 0 {
|
||||
v.Set(utils.PROP_DIAGNOSTIC_UNIT_TESTS, "1")
|
||||
} else {
|
||||
v.Set(utils.PROP_DIAGNOSTIC_UNIT_TESTS, "0")
|
||||
}
|
||||
|
||||
systemSecurityLastTime := &model.System{Name: model.SYSTEM_LAST_SECURITY_TIME, Value: strconv.FormatInt(currentTime, 10)}
|
||||
if lastSecurityTime == 0 {
|
||||
<-api.Srv.Store.System().Save(systemSecurityLastTime)
|
||||
} else {
|
||||
|
||||
@@ -8,6 +8,12 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
SYSTEM_DIAGNOSTIC_ID = "DiagnosticId"
|
||||
SYSTEM_RAN_UNIT_TESTS = "RanUnitTests"
|
||||
SYSTEM_LAST_SECURITY_TIME = "LastSecurityTime"
|
||||
)
|
||||
|
||||
type System struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
|
||||
@@ -16,17 +16,18 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-gorp/gorp"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"io"
|
||||
sqltrace "log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-gorp/gorp"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
)
|
||||
|
||||
type SqlStore struct {
|
||||
@@ -179,6 +180,17 @@ func (ss SqlStore) GetCurrentSchemaVersion() string {
|
||||
return version
|
||||
}
|
||||
|
||||
func (ss SqlStore) MarkSystemRanUnitTests() {
|
||||
if result := <-ss.System().Get(); result.Err == nil {
|
||||
props := result.Data.(model.StringMap)
|
||||
unitTests := props[model.SYSTEM_RAN_UNIT_TESTS]
|
||||
if len(unitTests) == 0 {
|
||||
systemTests := &model.System{Name: model.SYSTEM_RAN_UNIT_TESTS, Value: "1"}
|
||||
<-ss.System().Save(systemTests)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ss SqlStore) DoesTableExist(tableName string) bool {
|
||||
if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_POSTGRES {
|
||||
count, err := ss.GetMaster().SelectInt(
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
)
|
||||
|
||||
var store Store
|
||||
@@ -16,6 +17,8 @@ func Setup() {
|
||||
if store == nil {
|
||||
utils.LoadConfig("config.json")
|
||||
store = NewSqlStore()
|
||||
|
||||
store.MarkSystemRanUnitTests()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ type Store interface {
|
||||
System() SystemStore
|
||||
Webhook() WebhookStore
|
||||
Preference() PreferenceStore
|
||||
MarkSystemRanUnitTests()
|
||||
Close()
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ const (
|
||||
PROP_DIAGNOSTIC_OS = "os"
|
||||
PROP_DIAGNOSTIC_USER_COUNT = "uc"
|
||||
PROP_DIAGNOSTIC_ACTIVE_USER_COUNT = "auc"
|
||||
PROP_DIAGNOSTIC_UNIT_TESTS = "ut"
|
||||
)
|
||||
|
||||
func SendDiagnostic(values url.Values) {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
},
|
||||
"globals": {
|
||||
"React": false,
|
||||
"ReactDOM": false,
|
||||
"ReactBootstrap": false
|
||||
},
|
||||
"rules": {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See License.txt for license information.
|
||||
|
||||
var UserStore = require('../stores/user_store.jsx');
|
||||
var ChannelStore = require('../stores/channel_store.jsx');
|
||||
var AsyncClient = require('../utils/async_client.jsx');
|
||||
var LoadingScreen = require('./loading_screen.jsx');
|
||||
var Utils = require('../utils/utils.jsx');
|
||||
@@ -14,8 +15,10 @@ export default class AccessHistoryModal extends React.Component {
|
||||
this.handleMoreInfo = this.handleMoreInfo.bind(this);
|
||||
this.onHide = this.onHide.bind(this);
|
||||
this.onShow = this.onShow.bind(this);
|
||||
this.formatAuditInfo = this.formatAuditInfo.bind(this);
|
||||
this.handleRevokedSession = this.handleRevokedSession.bind(this);
|
||||
|
||||
let state = this.getStateFromStoresForAudits();
|
||||
const state = this.getStateFromStoresForAudits();
|
||||
state.moreInfo = [];
|
||||
|
||||
this.state = state;
|
||||
@@ -34,9 +37,9 @@ export default class AccessHistoryModal extends React.Component {
|
||||
}
|
||||
componentDidMount() {
|
||||
UserStore.addAuditsChangeListener(this.onAuditChange);
|
||||
$(React.findDOMNode(this.refs.modal)).on('shown.bs.modal', this.onShow);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', this.onShow);
|
||||
|
||||
$(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.onHide);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.onHide);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
UserStore.removeAuditsChangeListener(this.onAuditChange);
|
||||
@@ -52,23 +55,269 @@ export default class AccessHistoryModal extends React.Component {
|
||||
newMoreInfo[index] = true;
|
||||
this.setState({moreInfo: newMoreInfo});
|
||||
}
|
||||
handleRevokedSession(sessionId) {
|
||||
return 'The session with id ' + sessionId + ' was revoked';
|
||||
}
|
||||
formatAuditInfo(currentAudit) {
|
||||
const currentActionURL = currentAudit.action.replace(/\/api\/v[1-9]/, '');
|
||||
|
||||
let currentAuditDesc = '';
|
||||
|
||||
if (currentActionURL.indexOf('/channels') === 0) {
|
||||
const channelInfo = currentAudit.extra_info.split(' ');
|
||||
const channelNameField = channelInfo[0].split('=');
|
||||
|
||||
let channelURL = '';
|
||||
let channelObj;
|
||||
let channelName = '';
|
||||
if (channelNameField.indexOf('name') >= 0) {
|
||||
channelURL = channelNameField[channelNameField.indexOf('name') + 1];
|
||||
channelObj = ChannelStore.getByName(channelURL);
|
||||
if (channelObj) {
|
||||
channelName = channelObj.display_name;
|
||||
} else {
|
||||
channelName = channelURL;
|
||||
}
|
||||
}
|
||||
|
||||
switch (currentActionURL) {
|
||||
case '/channels/create':
|
||||
currentAuditDesc = 'Created the ' + channelName + ' channel/group';
|
||||
break;
|
||||
case '/channels/create_direct':
|
||||
currentAuditDesc = 'Established a direct message channel with ' + Utils.getDirectTeammate(channelObj.id).username;
|
||||
break;
|
||||
case '/channels/update':
|
||||
currentAuditDesc = 'Updated the ' + channelName + ' channel/group name';
|
||||
break;
|
||||
case '/channels/update_desc':
|
||||
currentAuditDesc = 'Updated the ' + channelName + ' channel/group description';
|
||||
break;
|
||||
default:
|
||||
let userIdField = [];
|
||||
let userId = '';
|
||||
let username = '';
|
||||
|
||||
if (channelInfo[1]) {
|
||||
userIdField = channelInfo[1].split('=');
|
||||
|
||||
if (userIdField.indexOf('user_id') >= 0) {
|
||||
userId = userIdField[userIdField.indexOf('user_id') + 1];
|
||||
username = UserStore.getProfile(userId).username;
|
||||
}
|
||||
}
|
||||
|
||||
if (/\/channels\/[A-Za-z0-9]+\/delete/.test(currentActionURL)) {
|
||||
currentAuditDesc = 'Deleted the channel/group with the URL ' + channelURL;
|
||||
} else if (/\/channels\/[A-Za-z0-9]+\/add/.test(currentActionURL)) {
|
||||
currentAuditDesc = 'Added ' + username + ' to the ' + channelName + ' channel/group';
|
||||
} else if (/\/channels\/[A-Za-z0-9]+\/remove/.test(currentActionURL)) {
|
||||
currentAuditDesc = 'Removed ' + username + ' from the ' + channelName + ' channel/group';
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} else if (currentActionURL.indexOf('/oauth') === 0) {
|
||||
const oauthInfo = currentAudit.extra_info.split(' ');
|
||||
|
||||
switch (currentActionURL) {
|
||||
case '/oauth/register':
|
||||
const clientIdField = oauthInfo[0].split('=');
|
||||
|
||||
if (clientIdField[0] === 'client_id') {
|
||||
currentAuditDesc = 'Attempted to register a new OAuth Application with ID ' + clientIdField[1];
|
||||
}
|
||||
|
||||
break;
|
||||
case '/oauth/allow':
|
||||
if (oauthInfo[0] === 'attempt') {
|
||||
currentAuditDesc = 'Attempted to allow a new OAuth service access';
|
||||
} else if (oauthInfo[0] === 'success') {
|
||||
currentAuditDesc = 'Successfully gave a new OAuth service access';
|
||||
} else if (oauthInfo[0] === 'fail - redirect_uri did not match registered callback') {
|
||||
currentAuditDesc = 'Failed to allow a new OAuth service access - the redirect URI did not match the previously registered callback';
|
||||
}
|
||||
|
||||
break;
|
||||
case '/oauth/access_token':
|
||||
if (oauthInfo[0] === 'attempt') {
|
||||
currentAuditDesc = 'Attempted to get an OAuth access token';
|
||||
} else if (oauthInfo[0] === 'success') {
|
||||
currentAuditDesc = 'Successfully added a new OAuth service';
|
||||
} else {
|
||||
const oauthTokenFailure = oauthInfo[0].split('-');
|
||||
|
||||
if (oauthTokenFailure[0].trim() === 'fail' && oauthTokenFailure[1]) {
|
||||
currentAuditDesc = 'Failed to get an OAuth access token - ' + oauthTokenFailure[1].trim();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (currentActionURL.indexOf('/users') === 0) {
|
||||
const userInfo = currentAudit.extra_info.split(' ');
|
||||
|
||||
switch (currentActionURL) {
|
||||
case '/users/login':
|
||||
if (userInfo[0] === 'attempt') {
|
||||
currentAuditDesc = 'Attempted to login';
|
||||
} else if (userInfo[0] === 'success') {
|
||||
currentAuditDesc = 'Successfully logged in';
|
||||
} else if (userInfo[0]) {
|
||||
currentAuditDesc = 'FAILED login attempt';
|
||||
}
|
||||
|
||||
break;
|
||||
case '/users/revoke_session':
|
||||
currentAuditDesc = this.handleRevokedSession(userInfo[0].split('=')[1]);
|
||||
break;
|
||||
case '/users/newimage':
|
||||
currentAuditDesc = 'Updated your profile picture';
|
||||
break;
|
||||
case '/users/update':
|
||||
currentAuditDesc = 'Updated the general settings of your account';
|
||||
break;
|
||||
case '/users/newpassword':
|
||||
if (userInfo[0] === 'attempted') {
|
||||
currentAuditDesc = 'Attempted to change password';
|
||||
} else if (userInfo[0] === 'completed') {
|
||||
currentAuditDesc = 'Successfully changed password';
|
||||
} else if (userInfo[0] === 'failed - tried to update user password who was logged in through oauth') {
|
||||
currentAuditDesc = 'Failed to change password - tried to update user password who was logged in through oauth';
|
||||
}
|
||||
|
||||
break;
|
||||
case '/users/update_roles':
|
||||
const userRoles = userInfo[0].split('=')[1];
|
||||
|
||||
currentAuditDesc = 'Updated user role(s) to ';
|
||||
if (userRoles.trim()) {
|
||||
currentAuditDesc += userRoles;
|
||||
} else {
|
||||
currentAuditDesc += 'member';
|
||||
}
|
||||
|
||||
break;
|
||||
case '/users/update_active':
|
||||
const updateType = userInfo[0].split('=')[0];
|
||||
const updateField = userInfo[0].split('=')[1];
|
||||
|
||||
/* Either describes account activation/deactivation or a revoked session as part of an account deactivation */
|
||||
if (updateType === 'active') {
|
||||
if (updateField === 'true') {
|
||||
currentAuditDesc = 'Account made active';
|
||||
} else if (updateField === 'false') {
|
||||
currentAuditDesc = 'Account made inactive';
|
||||
}
|
||||
|
||||
const actingUserInfo = userInfo[1].split('=');
|
||||
if (actingUserInfo[0] === 'session_user') {
|
||||
const actingUser = UserStore.getProfile(actingUserInfo[1]);
|
||||
const currentUser = UserStore.getCurrentUser();
|
||||
if (currentUser && actingUser && (Utils.isAdmin(currentUser.roles) || Utils.isSystemAdmin(currentUser.roles))) {
|
||||
currentAuditDesc += ' by ' + actingUser.username;
|
||||
} else if (currentUser && actingUser) {
|
||||
currentAuditDesc += ' by an admin';
|
||||
}
|
||||
}
|
||||
} else if (updateType === 'session_id') {
|
||||
currentAuditDesc = this.handleRevokedSession(updateField);
|
||||
}
|
||||
|
||||
break;
|
||||
case '/users/send_password_reset':
|
||||
currentAuditDesc = 'Sent an email to ' + userInfo[0].split('=')[1] + ' to reset your password';
|
||||
break;
|
||||
case '/users/reset_password':
|
||||
if (userInfo[0] === 'attempt') {
|
||||
currentAuditDesc = 'Attempted to reset password';
|
||||
} else if (userInfo[0] === 'success') {
|
||||
currentAuditDesc = 'Successfully reset password';
|
||||
}
|
||||
|
||||
break;
|
||||
case '/users/update_notify':
|
||||
currentAuditDesc = 'Updated your global notification settings';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (currentActionURL.indexOf('/hooks') === 0) {
|
||||
const webhookInfo = currentAudit.extra_info.split(' ');
|
||||
|
||||
switch (currentActionURL) {
|
||||
case '/hooks/incoming/create':
|
||||
if (webhookInfo[0] === 'attempt') {
|
||||
currentAuditDesc = 'Attempted to create a webhook';
|
||||
} else if (webhookInfo[0] === 'success') {
|
||||
currentAuditDesc = 'Successfully created a webhook';
|
||||
} else if (webhookInfo[0] === 'fail - bad channel permissions') {
|
||||
currentAuditDesc = 'Failed to create a webhook - bad channel permissions';
|
||||
}
|
||||
|
||||
break;
|
||||
case '/hooks/incoming/delete':
|
||||
if (webhookInfo[0] === 'attempt') {
|
||||
currentAuditDesc = 'Attempted to delete a webhook';
|
||||
} else if (webhookInfo[0] === 'success') {
|
||||
currentAuditDesc = 'Successfully deleted a webhook';
|
||||
} else if (webhookInfo[0] === 'fail - inappropriate conditions') {
|
||||
currentAuditDesc = 'Failed to delete a webhook - inappropriate conditions';
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (currentActionURL) {
|
||||
case '/logout':
|
||||
currentAuditDesc = 'Logged out of your account';
|
||||
break;
|
||||
case '/verify_email':
|
||||
currentAuditDesc = 'Sucessfully verified your email address';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If all else fails... */
|
||||
if (!currentAuditDesc) {
|
||||
/* Currently not called anywhere */
|
||||
if (currentAudit.extra_info.indexOf('revoked_all=') >= 0) {
|
||||
currentAuditDesc = 'Revoked all current sessions for the team';
|
||||
} else {
|
||||
let currentActionDesc = '';
|
||||
if (currentActionURL && currentActionURL.lastIndexOf('/') !== -1) {
|
||||
currentActionDesc = currentActionURL.substring(currentActionURL.lastIndexOf('/') + 1).replace('_', ' ');
|
||||
currentActionDesc = Utils.toTitleCase(currentActionDesc);
|
||||
}
|
||||
|
||||
let currentExtraInfoDesc = '';
|
||||
if (currentAudit.extra_info) {
|
||||
currentExtraInfoDesc = currentAudit.extra_info;
|
||||
|
||||
if (currentExtraInfoDesc.indexOf('=') !== -1) {
|
||||
currentExtraInfoDesc = currentExtraInfoDesc.substring(currentExtraInfoDesc.indexOf('=') + 1);
|
||||
}
|
||||
}
|
||||
currentAuditDesc = currentActionDesc + ' ' + currentExtraInfoDesc;
|
||||
}
|
||||
}
|
||||
|
||||
const currentDate = new Date(currentAudit.create_at);
|
||||
const currentAuditInfo = currentDate.toDateString() + ' - ' + currentDate.toLocaleTimeString(navigator.language, {hour: '2-digit', minute: '2-digit'}) + ' | ' + currentAuditDesc;
|
||||
return currentAuditInfo;
|
||||
}
|
||||
render() {
|
||||
var accessList = [];
|
||||
var currentHistoryDate = null;
|
||||
|
||||
for (var i = 0; i < this.state.audits.length; i++) {
|
||||
var currentAudit = this.state.audits[i];
|
||||
var newHistoryDate = new Date(currentAudit.create_at);
|
||||
var newDate = null;
|
||||
|
||||
if (!currentHistoryDate || currentHistoryDate.toLocaleDateString() !== newHistoryDate.toLocaleDateString()) {
|
||||
currentHistoryDate = newHistoryDate;
|
||||
newDate = (<div> {currentHistoryDate.toDateString()} </div>);
|
||||
}
|
||||
|
||||
if (!currentAudit.session_id && currentAudit.action.search('/users/login') !== -1) {
|
||||
currentAudit.session_id = 'N/A (Login attempt)';
|
||||
}
|
||||
const currentAudit = this.state.audits[i];
|
||||
const currentAuditInfo = this.formatAuditInfo(currentAudit);
|
||||
|
||||
var moreInfo = (
|
||||
<a
|
||||
@@ -76,15 +325,27 @@ export default class AccessHistoryModal extends React.Component {
|
||||
className='theme'
|
||||
onClick={this.handleMoreInfo.bind(this, i)}
|
||||
>
|
||||
More info
|
||||
{'More info'}
|
||||
</a>
|
||||
);
|
||||
|
||||
if (this.state.moreInfo[i]) {
|
||||
if (!currentAudit.session_id) {
|
||||
currentAudit.session_id = 'N/A';
|
||||
|
||||
if (currentAudit.action.search('/users/login') >= 0) {
|
||||
if (currentAudit.extra_info === 'attempt') {
|
||||
currentAudit.session_id += ' (Login attempt)';
|
||||
} else {
|
||||
currentAudit.session_id += ' (Login failure)';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
moreInfo = (
|
||||
<div>
|
||||
<div>{'IP: ' + currentAudit.ip_address}</div>
|
||||
<div>{'Session ID: ' + currentAudit.session_id}</div>
|
||||
<div>{'URL: ' + currentAudit.action.replace(/\/api\/v[1-9]/, '')}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -99,11 +360,9 @@ export default class AccessHistoryModal extends React.Component {
|
||||
key={'accessHistoryEntryKey' + i}
|
||||
className='access-history__table'
|
||||
>
|
||||
<div className='access__date'>{newDate}</div>
|
||||
<div className='access__report'>
|
||||
<div className='report__time'>{newHistoryDate.toLocaleTimeString(navigator.language, {hour: '2-digit', minute: '2-digit'})}</div>
|
||||
<div className='report__time'>{currentAuditInfo}</div>
|
||||
<div className='report__info'>
|
||||
<div>{'IP: ' + currentAudit.ip_address}</div>
|
||||
{moreInfo}
|
||||
</div>
|
||||
{divider}
|
||||
@@ -138,13 +397,13 @@ export default class AccessHistoryModal extends React.Component {
|
||||
data-dismiss='modal'
|
||||
aria-label='Close'
|
||||
>
|
||||
<span aria-hidden='true'>×</span>
|
||||
<span aria-hidden='true'>{'×'}</span>
|
||||
</button>
|
||||
<h4
|
||||
className='modal-title'
|
||||
id='myModalLabel'
|
||||
>
|
||||
Access History
|
||||
{'Access History'}
|
||||
</h4>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -56,9 +56,9 @@ export default class ActivityLogModal extends React.Component {
|
||||
}
|
||||
componentDidMount() {
|
||||
UserStore.addSessionsChangeListener(this.onListenerChange);
|
||||
$(React.findDOMNode(this.refs.modal)).on('shown.bs.modal', this.onShow);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', this.onShow);
|
||||
|
||||
$(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.onHide);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.onHide);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
UserStore.removeSessionsChangeListener(this.onListenerChange);
|
||||
|
||||
@@ -27,7 +27,7 @@ export default class AdminNavbarDropdown extends React.Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
$(React.findDOMNode(this.refs.dropdown)).on('hide.bs.dropdown', () => {
|
||||
$(ReactDOM.findDOMNode(this.refs.dropdown)).on('hide.bs.dropdown', () => {
|
||||
this.blockToggle = true;
|
||||
setTimeout(() => {
|
||||
this.blockToggle = false;
|
||||
@@ -36,7 +36,7 @@ export default class AdminNavbarDropdown extends React.Component {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
$(React.findDOMNode(this.refs.dropdown)).off('hide.bs.dropdown');
|
||||
$(ReactDOM.findDOMNode(this.refs.dropdown)).off('hide.bs.dropdown');
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -100,6 +100,7 @@ export default class AdminSidebar extends React.Component {
|
||||
className='menu-icon--right menu__close'
|
||||
onClick={this.removeTeam.bind(this, team.id)}
|
||||
style={{cursor: 'pointer'}}
|
||||
title='Remove team from sidebar menu'
|
||||
>
|
||||
{'x'}
|
||||
</span>
|
||||
@@ -233,7 +234,10 @@ export default class AdminSidebar extends React.Component {
|
||||
href='#'
|
||||
onClick={this.showTeamSelect}
|
||||
>
|
||||
<i className='fa fa-plus'></i>
|
||||
<i
|
||||
className='fa fa-plus'
|
||||
title='Add team to sidebar menu'
|
||||
></i>
|
||||
</a>
|
||||
</span>
|
||||
</h4>
|
||||
|
||||
@@ -41,28 +41,28 @@ export default class EmailSettings extends React.Component {
|
||||
|
||||
buildConfig() {
|
||||
var config = this.props.config;
|
||||
config.EmailSettings.EnableSignUpWithEmail = React.findDOMNode(this.refs.allowSignUpWithEmail).checked;
|
||||
config.EmailSettings.SendEmailNotifications = React.findDOMNode(this.refs.sendEmailNotifications).checked;
|
||||
config.EmailSettings.RequireEmailVerification = React.findDOMNode(this.refs.requireEmailVerification).checked;
|
||||
config.EmailSettings.SendEmailNotifications = React.findDOMNode(this.refs.sendEmailNotifications).checked;
|
||||
config.EmailSettings.FeedbackName = React.findDOMNode(this.refs.feedbackName).value.trim();
|
||||
config.EmailSettings.FeedbackEmail = React.findDOMNode(this.refs.feedbackEmail).value.trim();
|
||||
config.EmailSettings.SMTPServer = React.findDOMNode(this.refs.SMTPServer).value.trim();
|
||||
config.EmailSettings.SMTPPort = React.findDOMNode(this.refs.SMTPPort).value.trim();
|
||||
config.EmailSettings.SMTPUsername = React.findDOMNode(this.refs.SMTPUsername).value.trim();
|
||||
config.EmailSettings.SMTPPassword = React.findDOMNode(this.refs.SMTPPassword).value.trim();
|
||||
config.EmailSettings.ConnectionSecurity = React.findDOMNode(this.refs.ConnectionSecurity).value.trim();
|
||||
config.EmailSettings.EnableSignUpWithEmail = ReactDOM.findDOMNode(this.refs.allowSignUpWithEmail).checked;
|
||||
config.EmailSettings.SendEmailNotifications = ReactDOM.findDOMNode(this.refs.sendEmailNotifications).checked;
|
||||
config.EmailSettings.RequireEmailVerification = ReactDOM.findDOMNode(this.refs.requireEmailVerification).checked;
|
||||
config.EmailSettings.SendEmailNotifications = ReactDOM.findDOMNode(this.refs.sendEmailNotifications).checked;
|
||||
config.EmailSettings.FeedbackName = ReactDOM.findDOMNode(this.refs.feedbackName).value.trim();
|
||||
config.EmailSettings.FeedbackEmail = ReactDOM.findDOMNode(this.refs.feedbackEmail).value.trim();
|
||||
config.EmailSettings.SMTPServer = ReactDOM.findDOMNode(this.refs.SMTPServer).value.trim();
|
||||
config.EmailSettings.SMTPPort = ReactDOM.findDOMNode(this.refs.SMTPPort).value.trim();
|
||||
config.EmailSettings.SMTPUsername = ReactDOM.findDOMNode(this.refs.SMTPUsername).value.trim();
|
||||
config.EmailSettings.SMTPPassword = ReactDOM.findDOMNode(this.refs.SMTPPassword).value.trim();
|
||||
config.EmailSettings.ConnectionSecurity = ReactDOM.findDOMNode(this.refs.ConnectionSecurity).value.trim();
|
||||
|
||||
config.EmailSettings.InviteSalt = React.findDOMNode(this.refs.InviteSalt).value.trim();
|
||||
config.EmailSettings.InviteSalt = ReactDOM.findDOMNode(this.refs.InviteSalt).value.trim();
|
||||
if (config.EmailSettings.InviteSalt === '') {
|
||||
config.EmailSettings.InviteSalt = crypto.randomBytes(256).toString('base64').substring(0, 32);
|
||||
React.findDOMNode(this.refs.InviteSalt).value = config.EmailSettings.InviteSalt;
|
||||
ReactDOM.findDOMNode(this.refs.InviteSalt).value = config.EmailSettings.InviteSalt;
|
||||
}
|
||||
|
||||
config.EmailSettings.PasswordResetSalt = React.findDOMNode(this.refs.PasswordResetSalt).value.trim();
|
||||
config.EmailSettings.PasswordResetSalt = ReactDOM.findDOMNode(this.refs.PasswordResetSalt).value.trim();
|
||||
if (config.EmailSettings.PasswordResetSalt === '') {
|
||||
config.EmailSettings.PasswordResetSalt = crypto.randomBytes(256).toString('base64').substring(0, 32);
|
||||
React.findDOMNode(this.refs.PasswordResetSalt).value = config.EmailSettings.PasswordResetSalt;
|
||||
ReactDOM.findDOMNode(this.refs.PasswordResetSalt).value = config.EmailSettings.PasswordResetSalt;
|
||||
}
|
||||
|
||||
return config;
|
||||
@@ -70,14 +70,14 @@ export default class EmailSettings extends React.Component {
|
||||
|
||||
handleGenerateInvite(e) {
|
||||
e.preventDefault();
|
||||
React.findDOMNode(this.refs.InviteSalt).value = crypto.randomBytes(256).toString('base64').substring(0, 32);
|
||||
ReactDOM.findDOMNode(this.refs.InviteSalt).value = crypto.randomBytes(256).toString('base64').substring(0, 32);
|
||||
var s = {saveNeeded: true, serverError: this.state.serverError};
|
||||
this.setState(s);
|
||||
}
|
||||
|
||||
handleGenerateReset(e) {
|
||||
e.preventDefault();
|
||||
React.findDOMNode(this.refs.PasswordResetSalt).value = crypto.randomBytes(256).toString('base64').substring(0, 32);
|
||||
ReactDOM.findDOMNode(this.refs.PasswordResetSalt).value = crypto.randomBytes(256).toString('base64').substring(0, 32);
|
||||
var s = {saveNeeded: true, serverError: this.state.serverError};
|
||||
this.setState(s);
|
||||
}
|
||||
@@ -447,7 +447,7 @@ export default class EmailSettings extends React.Component {
|
||||
</div>
|
||||
<div className='help-text'>
|
||||
<button
|
||||
className='btn'
|
||||
className='btn btn-default'
|
||||
onClick={this.handleTestConnection}
|
||||
disabled={!this.state.sendEmailNotifications}
|
||||
id='connection-button'
|
||||
@@ -482,7 +482,7 @@ export default class EmailSettings extends React.Component {
|
||||
<p className='help-text'>{'32-character salt added to signing of email invites. Randomly generated on install. Click "Re-Generate" to create new salt.'}</p>
|
||||
<div className='help-text'>
|
||||
<button
|
||||
className='btn'
|
||||
className='btn btn-default'
|
||||
onClick={this.handleGenerateInvite}
|
||||
disabled={!this.state.sendEmailNotifications}
|
||||
>
|
||||
@@ -513,7 +513,7 @@ export default class EmailSettings extends React.Component {
|
||||
<p className='help-text'>{'32-character salt added to signing of password reset emails. Randomly generated on install. Click "Re-Generate" to create new salt.'}</p>
|
||||
<div className='help-text'>
|
||||
<button
|
||||
className='btn'
|
||||
className='btn btn-default'
|
||||
onClick={this.handleGenerateReset}
|
||||
disabled={!this.state.sendEmailNotifications}
|
||||
>
|
||||
|
||||
@@ -37,12 +37,12 @@ export default class GitLabSettings extends React.Component {
|
||||
$('#save-button').button('loading');
|
||||
|
||||
var config = this.props.config;
|
||||
config.GitLabSettings.Enable = React.findDOMNode(this.refs.Enable).checked;
|
||||
config.GitLabSettings.Secret = React.findDOMNode(this.refs.Secret).value.trim();
|
||||
config.GitLabSettings.Id = React.findDOMNode(this.refs.Id).value.trim();
|
||||
config.GitLabSettings.AuthEndpoint = React.findDOMNode(this.refs.AuthEndpoint).value.trim();
|
||||
config.GitLabSettings.TokenEndpoint = React.findDOMNode(this.refs.TokenEndpoint).value.trim();
|
||||
config.GitLabSettings.UserApiEndpoint = React.findDOMNode(this.refs.UserApiEndpoint).value.trim();
|
||||
config.GitLabSettings.Enable = ReactDOM.findDOMNode(this.refs.Enable).checked;
|
||||
config.GitLabSettings.Secret = ReactDOM.findDOMNode(this.refs.Secret).value.trim();
|
||||
config.GitLabSettings.Id = ReactDOM.findDOMNode(this.refs.Id).value.trim();
|
||||
config.GitLabSettings.AuthEndpoint = ReactDOM.findDOMNode(this.refs.AuthEndpoint).value.trim();
|
||||
config.GitLabSettings.TokenEndpoint = ReactDOM.findDOMNode(this.refs.TokenEndpoint).value.trim();
|
||||
config.GitLabSettings.UserApiEndpoint = ReactDOM.findDOMNode(this.refs.UserApiEndpoint).value.trim();
|
||||
|
||||
Client.saveConfig(
|
||||
config,
|
||||
@@ -116,12 +116,14 @@ export default class GitLabSettings extends React.Component {
|
||||
<p className='help-text'>
|
||||
{'When true, Mattermost allows team creation and account signup using GitLab OAuth.'} <br/>
|
||||
</p>
|
||||
<ol className='help-text'>
|
||||
<li>{'Log in to your GitLab account and go to Applications -> Profile Settings.'}</li>
|
||||
<li>{'Enter Redirect URIs "<your-mattermost-url>/login/gitlab/complete" (example: http://localhost:8065/login/gitlab/complete) and "<your-mattermost-url>/signup/gitlab/complete". '}</li>
|
||||
<li>{'Then use "Secret" and "Id" fields from GitLab to complete the options below.'}</li>
|
||||
<li>{'Complete the Endpoint URLs below. '}</li>
|
||||
</ol>
|
||||
<div className='help-text'>
|
||||
<ol>
|
||||
<li>{'Log in to your GitLab account and go to Applications -> Profile Settings.'}</li>
|
||||
<li>{'Enter Redirect URIs "<your-mattermost-url>/login/gitlab/complete" (example: http://localhost:8065/login/gitlab/complete) and "<your-mattermost-url>/signup/gitlab/complete". '}</li>
|
||||
<li>{'Then use "Secret" and "Id" fields from GitLab to complete the options below.'}</li>
|
||||
<li>{'Complete the Endpoint URLs below. '}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -258,7 +260,7 @@ export default class GitLabSettings extends React.Component {
|
||||
}
|
||||
|
||||
|
||||
//config.GitLabSettings.Scope = React.findDOMNode(this.refs.Scope).value.trim();
|
||||
//config.GitLabSettings.Scope = ReactDOM.findDOMNode(this.refs.Scope).value.trim();
|
||||
// <div className='form-group'>
|
||||
// <label
|
||||
// className='control-label col-sm-4'
|
||||
|
||||
@@ -24,7 +24,7 @@ export default class FileSettings extends React.Component {
|
||||
var s = {saveNeeded: true, serverError: this.state.serverError};
|
||||
|
||||
if (action === 'DriverName') {
|
||||
s.DriverName = React.findDOMNode(this.refs.DriverName).value;
|
||||
s.DriverName = ReactDOM.findDOMNode(this.refs.DriverName).value;
|
||||
}
|
||||
|
||||
this.setState(s);
|
||||
@@ -32,7 +32,7 @@ export default class FileSettings extends React.Component {
|
||||
|
||||
handleGenerate(e) {
|
||||
e.preventDefault();
|
||||
React.findDOMNode(this.refs.PublicLinkSalt).value = crypto.randomBytes(256).toString('base64').substring(0, 32);
|
||||
ReactDOM.findDOMNode(this.refs.PublicLinkSalt).value = crypto.randomBytes(256).toString('base64').substring(0, 32);
|
||||
var s = {saveNeeded: true, serverError: this.state.serverError};
|
||||
this.setState(s);
|
||||
}
|
||||
@@ -42,62 +42,62 @@ export default class FileSettings extends React.Component {
|
||||
$('#save-button').button('loading');
|
||||
|
||||
var config = this.props.config;
|
||||
config.FileSettings.DriverName = React.findDOMNode(this.refs.DriverName).value;
|
||||
config.FileSettings.Directory = React.findDOMNode(this.refs.Directory).value;
|
||||
config.FileSettings.AmazonS3AccessKeyId = React.findDOMNode(this.refs.AmazonS3AccessKeyId).value;
|
||||
config.FileSettings.AmazonS3SecretAccessKey = React.findDOMNode(this.refs.AmazonS3SecretAccessKey).value;
|
||||
config.FileSettings.AmazonS3Bucket = React.findDOMNode(this.refs.AmazonS3Bucket).value;
|
||||
config.FileSettings.AmazonS3Region = React.findDOMNode(this.refs.AmazonS3Region).value;
|
||||
config.FileSettings.EnablePublicLink = React.findDOMNode(this.refs.EnablePublicLink).checked;
|
||||
config.FileSettings.DriverName = ReactDOM.findDOMNode(this.refs.DriverName).value;
|
||||
config.FileSettings.Directory = ReactDOM.findDOMNode(this.refs.Directory).value;
|
||||
config.FileSettings.AmazonS3AccessKeyId = ReactDOM.findDOMNode(this.refs.AmazonS3AccessKeyId).value;
|
||||
config.FileSettings.AmazonS3SecretAccessKey = ReactDOM.findDOMNode(this.refs.AmazonS3SecretAccessKey).value;
|
||||
config.FileSettings.AmazonS3Bucket = ReactDOM.findDOMNode(this.refs.AmazonS3Bucket).value;
|
||||
config.FileSettings.AmazonS3Region = ReactDOM.findDOMNode(this.refs.AmazonS3Region).value;
|
||||
config.FileSettings.EnablePublicLink = ReactDOM.findDOMNode(this.refs.EnablePublicLink).checked;
|
||||
|
||||
config.FileSettings.PublicLinkSalt = React.findDOMNode(this.refs.PublicLinkSalt).value.trim();
|
||||
config.FileSettings.PublicLinkSalt = ReactDOM.findDOMNode(this.refs.PublicLinkSalt).value.trim();
|
||||
|
||||
if (config.FileSettings.PublicLinkSalt === '') {
|
||||
config.FileSettings.PublicLinkSalt = crypto.randomBytes(256).toString('base64').substring(0, 32);
|
||||
React.findDOMNode(this.refs.PublicLinkSalt).value = config.FileSettings.PublicLinkSalt;
|
||||
ReactDOM.findDOMNode(this.refs.PublicLinkSalt).value = config.FileSettings.PublicLinkSalt;
|
||||
}
|
||||
|
||||
var thumbnailWidth = 120;
|
||||
if (!isNaN(parseInt(React.findDOMNode(this.refs.ThumbnailWidth).value, 10))) {
|
||||
thumbnailWidth = parseInt(React.findDOMNode(this.refs.ThumbnailWidth).value, 10);
|
||||
if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.ThumbnailWidth).value, 10))) {
|
||||
thumbnailWidth = parseInt(ReactDOM.findDOMNode(this.refs.ThumbnailWidth).value, 10);
|
||||
}
|
||||
config.FileSettings.ThumbnailWidth = thumbnailWidth;
|
||||
React.findDOMNode(this.refs.ThumbnailWidth).value = thumbnailWidth;
|
||||
ReactDOM.findDOMNode(this.refs.ThumbnailWidth).value = thumbnailWidth;
|
||||
|
||||
var thumbnailHeight = 100;
|
||||
if (!isNaN(parseInt(React.findDOMNode(this.refs.ThumbnailHeight).value, 10))) {
|
||||
thumbnailHeight = parseInt(React.findDOMNode(this.refs.ThumbnailHeight).value, 10);
|
||||
if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.ThumbnailHeight).value, 10))) {
|
||||
thumbnailHeight = parseInt(ReactDOM.findDOMNode(this.refs.ThumbnailHeight).value, 10);
|
||||
}
|
||||
config.FileSettings.ThumbnailHeight = thumbnailHeight;
|
||||
React.findDOMNode(this.refs.ThumbnailHeight).value = thumbnailHeight;
|
||||
ReactDOM.findDOMNode(this.refs.ThumbnailHeight).value = thumbnailHeight;
|
||||
|
||||
var previewWidth = 1024;
|
||||
if (!isNaN(parseInt(React.findDOMNode(this.refs.PreviewWidth).value, 10))) {
|
||||
previewWidth = parseInt(React.findDOMNode(this.refs.PreviewWidth).value, 10);
|
||||
if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.PreviewWidth).value, 10))) {
|
||||
previewWidth = parseInt(ReactDOM.findDOMNode(this.refs.PreviewWidth).value, 10);
|
||||
}
|
||||
config.FileSettings.PreviewWidth = previewWidth;
|
||||
React.findDOMNode(this.refs.PreviewWidth).value = previewWidth;
|
||||
ReactDOM.findDOMNode(this.refs.PreviewWidth).value = previewWidth;
|
||||
|
||||
var previewHeight = 0;
|
||||
if (!isNaN(parseInt(React.findDOMNode(this.refs.PreviewHeight).value, 10))) {
|
||||
previewHeight = parseInt(React.findDOMNode(this.refs.PreviewHeight).value, 10);
|
||||
if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.PreviewHeight).value, 10))) {
|
||||
previewHeight = parseInt(ReactDOM.findDOMNode(this.refs.PreviewHeight).value, 10);
|
||||
}
|
||||
config.FileSettings.PreviewHeight = previewHeight;
|
||||
React.findDOMNode(this.refs.PreviewHeight).value = previewHeight;
|
||||
ReactDOM.findDOMNode(this.refs.PreviewHeight).value = previewHeight;
|
||||
|
||||
var profileWidth = 128;
|
||||
if (!isNaN(parseInt(React.findDOMNode(this.refs.ProfileWidth).value, 10))) {
|
||||
profileWidth = parseInt(React.findDOMNode(this.refs.ProfileWidth).value, 10);
|
||||
if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.ProfileWidth).value, 10))) {
|
||||
profileWidth = parseInt(ReactDOM.findDOMNode(this.refs.ProfileWidth).value, 10);
|
||||
}
|
||||
config.FileSettings.ProfileWidth = profileWidth;
|
||||
React.findDOMNode(this.refs.ProfileWidth).value = profileWidth;
|
||||
ReactDOM.findDOMNode(this.refs.ProfileWidth).value = profileWidth;
|
||||
|
||||
var profileHeight = 128;
|
||||
if (!isNaN(parseInt(React.findDOMNode(this.refs.ProfileHeight).value, 10))) {
|
||||
profileHeight = parseInt(React.findDOMNode(this.refs.ProfileHeight).value, 10);
|
||||
if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.ProfileHeight).value, 10))) {
|
||||
profileHeight = parseInt(ReactDOM.findDOMNode(this.refs.ProfileHeight).value, 10);
|
||||
}
|
||||
config.FileSettings.ProfileHeight = profileHeight;
|
||||
React.findDOMNode(this.refs.ProfileHeight).value = profileHeight;
|
||||
ReactDOM.findDOMNode(this.refs.ProfileHeight).value = profileHeight;
|
||||
|
||||
Client.saveConfig(
|
||||
config,
|
||||
|
||||
@@ -46,12 +46,12 @@ export default class LogSettings extends React.Component {
|
||||
$('#save-button').button('loading');
|
||||
|
||||
var config = this.props.config;
|
||||
config.LogSettings.EnableConsole = React.findDOMNode(this.refs.consoleEnable).checked;
|
||||
config.LogSettings.ConsoleLevel = React.findDOMNode(this.refs.consoleLevel).value;
|
||||
config.LogSettings.EnableFile = React.findDOMNode(this.refs.fileEnable).checked;
|
||||
config.LogSettings.FileLevel = React.findDOMNode(this.refs.fileLevel).value;
|
||||
config.LogSettings.FileLocation = React.findDOMNode(this.refs.fileLocation).value.trim();
|
||||
config.LogSettings.FileFormat = React.findDOMNode(this.refs.fileFormat).value.trim();
|
||||
config.LogSettings.EnableConsole = ReactDOM.findDOMNode(this.refs.consoleEnable).checked;
|
||||
config.LogSettings.ConsoleLevel = ReactDOM.findDOMNode(this.refs.consoleLevel).value;
|
||||
config.LogSettings.EnableFile = ReactDOM.findDOMNode(this.refs.fileEnable).checked;
|
||||
config.LogSettings.FileLevel = ReactDOM.findDOMNode(this.refs.fileLevel).value;
|
||||
config.LogSettings.FileLocation = ReactDOM.findDOMNode(this.refs.fileLocation).value.trim();
|
||||
config.LogSettings.FileFormat = ReactDOM.findDOMNode(this.refs.fileFormat).value.trim();
|
||||
|
||||
Client.saveConfig(
|
||||
config,
|
||||
|
||||
@@ -28,8 +28,8 @@ export default class PrivacySettings extends React.Component {
|
||||
$('#save-button').button('loading');
|
||||
|
||||
var config = this.props.config;
|
||||
config.PrivacySettings.ShowEmailAddress = React.findDOMNode(this.refs.ShowEmailAddress).checked;
|
||||
config.PrivacySettings.ShowFullName = React.findDOMNode(this.refs.ShowFullName).checked;
|
||||
config.PrivacySettings.ShowEmailAddress = ReactDOM.findDOMNode(this.refs.ShowEmailAddress).checked;
|
||||
config.PrivacySettings.ShowFullName = ReactDOM.findDOMNode(this.refs.ShowFullName).checked;
|
||||
|
||||
Client.saveConfig(
|
||||
config,
|
||||
|
||||
@@ -46,23 +46,23 @@ export default class RateSettings extends React.Component {
|
||||
$('#save-button').button('loading');
|
||||
|
||||
var config = this.props.config;
|
||||
config.RateLimitSettings.EnableRateLimiter = React.findDOMNode(this.refs.EnableRateLimiter).checked;
|
||||
config.RateLimitSettings.VaryByRemoteAddr = React.findDOMNode(this.refs.VaryByRemoteAddr).checked;
|
||||
config.RateLimitSettings.VaryByHeader = React.findDOMNode(this.refs.VaryByHeader).value.trim();
|
||||
config.RateLimitSettings.EnableRateLimiter = ReactDOM.findDOMNode(this.refs.EnableRateLimiter).checked;
|
||||
config.RateLimitSettings.VaryByRemoteAddr = ReactDOM.findDOMNode(this.refs.VaryByRemoteAddr).checked;
|
||||
config.RateLimitSettings.VaryByHeader = ReactDOM.findDOMNode(this.refs.VaryByHeader).value.trim();
|
||||
|
||||
var PerSec = 10;
|
||||
if (!isNaN(parseInt(React.findDOMNode(this.refs.PerSec).value, 10))) {
|
||||
PerSec = parseInt(React.findDOMNode(this.refs.PerSec).value, 10);
|
||||
if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.PerSec).value, 10))) {
|
||||
PerSec = parseInt(ReactDOM.findDOMNode(this.refs.PerSec).value, 10);
|
||||
}
|
||||
config.RateLimitSettings.PerSec = PerSec;
|
||||
React.findDOMNode(this.refs.PerSec).value = PerSec;
|
||||
ReactDOM.findDOMNode(this.refs.PerSec).value = PerSec;
|
||||
|
||||
var MemoryStoreSize = 10000;
|
||||
if (!isNaN(parseInt(React.findDOMNode(this.refs.MemoryStoreSize).value, 10))) {
|
||||
MemoryStoreSize = parseInt(React.findDOMNode(this.refs.MemoryStoreSize).value, 10);
|
||||
if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MemoryStoreSize).value, 10))) {
|
||||
MemoryStoreSize = parseInt(ReactDOM.findDOMNode(this.refs.MemoryStoreSize).value, 10);
|
||||
}
|
||||
config.RateLimitSettings.MemoryStoreSize = MemoryStoreSize;
|
||||
React.findDOMNode(this.refs.MemoryStoreSize).value = MemoryStoreSize;
|
||||
ReactDOM.findDOMNode(this.refs.MemoryStoreSize).value = MemoryStoreSize;
|
||||
|
||||
Client.saveConfig(
|
||||
config,
|
||||
|
||||
@@ -18,7 +18,7 @@ export default class ResetPasswordModal extends React.Component {
|
||||
|
||||
doSubmit(e) {
|
||||
e.preventDefault();
|
||||
var password = React.findDOMNode(this.refs.password).value;
|
||||
var password = ReactDOM.findDOMNode(this.refs.password).value;
|
||||
|
||||
if (!password || password.length < 5) {
|
||||
this.setState({serverError: 'Please enter at least 5 characters.'});
|
||||
@@ -34,7 +34,7 @@ export default class ResetPasswordModal extends React.Component {
|
||||
|
||||
Client.resetPassword(data,
|
||||
() => {
|
||||
this.props.onModalSubmit(React.findDOMNode(this.refs.password).value);
|
||||
this.props.onModalSubmit(ReactDOM.findDOMNode(this.refs.password).value);
|
||||
},
|
||||
(err) => {
|
||||
this.setState({serverError: err.message});
|
||||
|
||||
@@ -13,7 +13,7 @@ export default class SelectTeamModal extends React.Component {
|
||||
|
||||
doSubmit(e) {
|
||||
e.preventDefault();
|
||||
this.props.onModalSubmit(React.findDOMNode(this.refs.team).value);
|
||||
this.props.onModalSubmit(ReactDOM.findDOMNode(this.refs.team).value);
|
||||
}
|
||||
doCancel() {
|
||||
this.props.onModalDismissed();
|
||||
|
||||
@@ -27,28 +27,28 @@ export default class ServiceSettings extends React.Component {
|
||||
$('#save-button').button('loading');
|
||||
|
||||
var config = this.props.config;
|
||||
config.ServiceSettings.ListenAddress = React.findDOMNode(this.refs.ListenAddress).value.trim();
|
||||
config.ServiceSettings.ListenAddress = ReactDOM.findDOMNode(this.refs.ListenAddress).value.trim();
|
||||
if (config.ServiceSettings.ListenAddress === '') {
|
||||
config.ServiceSettings.ListenAddress = ':8065';
|
||||
React.findDOMNode(this.refs.ListenAddress).value = config.ServiceSettings.ListenAddress;
|
||||
ReactDOM.findDOMNode(this.refs.ListenAddress).value = config.ServiceSettings.ListenAddress;
|
||||
}
|
||||
|
||||
config.ServiceSettings.SegmentDeveloperKey = React.findDOMNode(this.refs.SegmentDeveloperKey).value.trim();
|
||||
config.ServiceSettings.GoogleDeveloperKey = React.findDOMNode(this.refs.GoogleDeveloperKey).value.trim();
|
||||
config.ServiceSettings.EnableIncomingWebhooks = React.findDOMNode(this.refs.EnableIncomingWebhooks).checked;
|
||||
config.ServiceSettings.EnablePostUsernameOverride = React.findDOMNode(this.refs.EnablePostUsernameOverride).checked;
|
||||
config.ServiceSettings.EnablePostIconOverride = React.findDOMNode(this.refs.EnablePostIconOverride).checked;
|
||||
config.ServiceSettings.EnableTesting = React.findDOMNode(this.refs.EnableTesting).checked;
|
||||
config.ServiceSettings.EnableSecurityFixAlert = React.findDOMNode(this.refs.EnableSecurityFixAlert).checked;
|
||||
config.ServiceSettings.SegmentDeveloperKey = ReactDOM.findDOMNode(this.refs.SegmentDeveloperKey).value.trim();
|
||||
config.ServiceSettings.GoogleDeveloperKey = ReactDOM.findDOMNode(this.refs.GoogleDeveloperKey).value.trim();
|
||||
config.ServiceSettings.EnableIncomingWebhooks = ReactDOM.findDOMNode(this.refs.EnableIncomingWebhooks).checked;
|
||||
config.ServiceSettings.EnablePostUsernameOverride = ReactDOM.findDOMNode(this.refs.EnablePostUsernameOverride).checked;
|
||||
config.ServiceSettings.EnablePostIconOverride = ReactDOM.findDOMNode(this.refs.EnablePostIconOverride).checked;
|
||||
config.ServiceSettings.EnableTesting = ReactDOM.findDOMNode(this.refs.EnableTesting).checked;
|
||||
config.ServiceSettings.EnableSecurityFixAlert = ReactDOM.findDOMNode(this.refs.EnableSecurityFixAlert).checked;
|
||||
|
||||
//config.ServiceSettings.EnableOAuthServiceProvider = React.findDOMNode(this.refs.EnableOAuthServiceProvider).checked;
|
||||
//config.ServiceSettings.EnableOAuthServiceProvider = ReactDOM.findDOMNode(this.refs.EnableOAuthServiceProvider).checked;
|
||||
|
||||
var MaximumLoginAttempts = 10;
|
||||
if (!isNaN(parseInt(React.findDOMNode(this.refs.MaximumLoginAttempts).value, 10))) {
|
||||
MaximumLoginAttempts = parseInt(React.findDOMNode(this.refs.MaximumLoginAttempts).value, 10);
|
||||
if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MaximumLoginAttempts).value, 10))) {
|
||||
MaximumLoginAttempts = parseInt(ReactDOM.findDOMNode(this.refs.MaximumLoginAttempts).value, 10);
|
||||
}
|
||||
config.ServiceSettings.MaximumLoginAttempts = MaximumLoginAttempts;
|
||||
React.findDOMNode(this.refs.MaximumLoginAttempts).value = MaximumLoginAttempts;
|
||||
ReactDOM.findDOMNode(this.refs.MaximumLoginAttempts).value = MaximumLoginAttempts;
|
||||
|
||||
Client.saveConfig(
|
||||
config,
|
||||
|
||||
@@ -29,27 +29,27 @@ export default class SqlSettings extends React.Component {
|
||||
$('#save-button').button('loading');
|
||||
|
||||
var config = this.props.config;
|
||||
config.SqlSettings.Trace = React.findDOMNode(this.refs.Trace).checked;
|
||||
config.SqlSettings.AtRestEncryptKey = React.findDOMNode(this.refs.AtRestEncryptKey).value.trim();
|
||||
config.SqlSettings.Trace = ReactDOM.findDOMNode(this.refs.Trace).checked;
|
||||
config.SqlSettings.AtRestEncryptKey = ReactDOM.findDOMNode(this.refs.AtRestEncryptKey).value.trim();
|
||||
|
||||
if (config.SqlSettings.AtRestEncryptKey === '') {
|
||||
config.SqlSettings.AtRestEncryptKey = crypto.randomBytes(256).toString('base64').substring(0, 32);
|
||||
React.findDOMNode(this.refs.AtRestEncryptKey).value = config.SqlSettings.AtRestEncryptKey;
|
||||
ReactDOM.findDOMNode(this.refs.AtRestEncryptKey).value = config.SqlSettings.AtRestEncryptKey;
|
||||
}
|
||||
|
||||
var MaxOpenConns = 10;
|
||||
if (!isNaN(parseInt(React.findDOMNode(this.refs.MaxOpenConns).value, 10))) {
|
||||
MaxOpenConns = parseInt(React.findDOMNode(this.refs.MaxOpenConns).value, 10);
|
||||
if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MaxOpenConns).value, 10))) {
|
||||
MaxOpenConns = parseInt(ReactDOM.findDOMNode(this.refs.MaxOpenConns).value, 10);
|
||||
}
|
||||
config.SqlSettings.MaxOpenConns = MaxOpenConns;
|
||||
React.findDOMNode(this.refs.MaxOpenConns).value = MaxOpenConns;
|
||||
ReactDOM.findDOMNode(this.refs.MaxOpenConns).value = MaxOpenConns;
|
||||
|
||||
var MaxIdleConns = 10;
|
||||
if (!isNaN(parseInt(React.findDOMNode(this.refs.MaxIdleConns).value, 10))) {
|
||||
MaxIdleConns = parseInt(React.findDOMNode(this.refs.MaxIdleConns).value, 10);
|
||||
if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MaxIdleConns).value, 10))) {
|
||||
MaxIdleConns = parseInt(ReactDOM.findDOMNode(this.refs.MaxIdleConns).value, 10);
|
||||
}
|
||||
config.SqlSettings.MaxIdleConns = MaxIdleConns;
|
||||
React.findDOMNode(this.refs.MaxIdleConns).value = MaxIdleConns;
|
||||
ReactDOM.findDOMNode(this.refs.MaxIdleConns).value = MaxIdleConns;
|
||||
|
||||
Client.saveConfig(
|
||||
config,
|
||||
@@ -79,7 +79,7 @@ export default class SqlSettings extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
React.findDOMNode(this.refs.AtRestEncryptKey).value = crypto.randomBytes(256).toString('base64').substring(0, 32);
|
||||
ReactDOM.findDOMNode(this.refs.AtRestEncryptKey).value = crypto.randomBytes(256).toString('base64').substring(0, 32);
|
||||
var s = {saveNeeded: true, serverError: this.state.serverError};
|
||||
this.setState(s);
|
||||
}
|
||||
@@ -220,7 +220,7 @@ export default class SqlSettings extends React.Component {
|
||||
<p className='help-text'>{'32-character salt available to encrypt and decrypt sensitive fields in database.'}</p>
|
||||
<div className='help-text'>
|
||||
<button
|
||||
className='help-link'
|
||||
className='btn btn-default'
|
||||
onClick={this.handleGenerate}
|
||||
>
|
||||
{'Re-Generate'}
|
||||
|
||||
@@ -27,17 +27,17 @@ export default class TeamSettings extends React.Component {
|
||||
$('#save-button').button('loading');
|
||||
|
||||
var config = this.props.config;
|
||||
config.TeamSettings.SiteName = React.findDOMNode(this.refs.SiteName).value.trim();
|
||||
config.TeamSettings.RestrictCreationToDomains = React.findDOMNode(this.refs.RestrictCreationToDomains).value.trim();
|
||||
config.TeamSettings.EnableTeamCreation = React.findDOMNode(this.refs.EnableTeamCreation).checked;
|
||||
config.TeamSettings.EnableUserCreation = React.findDOMNode(this.refs.EnableUserCreation).checked;
|
||||
config.TeamSettings.SiteName = ReactDOM.findDOMNode(this.refs.SiteName).value.trim();
|
||||
config.TeamSettings.RestrictCreationToDomains = ReactDOM.findDOMNode(this.refs.RestrictCreationToDomains).value.trim();
|
||||
config.TeamSettings.EnableTeamCreation = ReactDOM.findDOMNode(this.refs.EnableTeamCreation).checked;
|
||||
config.TeamSettings.EnableUserCreation = ReactDOM.findDOMNode(this.refs.EnableUserCreation).checked;
|
||||
|
||||
var MaxUsersPerTeam = 50;
|
||||
if (!isNaN(parseInt(React.findDOMNode(this.refs.MaxUsersPerTeam).value, 10))) {
|
||||
MaxUsersPerTeam = parseInt(React.findDOMNode(this.refs.MaxUsersPerTeam).value, 10);
|
||||
if (!isNaN(parseInt(ReactDOM.findDOMNode(this.refs.MaxUsersPerTeam).value, 10))) {
|
||||
MaxUsersPerTeam = parseInt(ReactDOM.findDOMNode(this.refs.MaxUsersPerTeam).value, 10);
|
||||
}
|
||||
config.TeamSettings.MaxUsersPerTeam = MaxUsersPerTeam;
|
||||
React.findDOMNode(this.refs.MaxUsersPerTeam).value = MaxUsersPerTeam;
|
||||
ReactDOM.findDOMNode(this.refs.MaxUsersPerTeam).value = MaxUsersPerTeam;
|
||||
|
||||
Client.saveConfig(
|
||||
config,
|
||||
|
||||
@@ -29,7 +29,7 @@ export default class ChangeUrlModal extends React.Component {
|
||||
}
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.show === true && prevProps.show === false) {
|
||||
React.findDOMNode(this.refs.urlinput).select();
|
||||
ReactDOM.findDOMNode(this.refs.urlinput).select();
|
||||
}
|
||||
}
|
||||
onURLChanged(e) {
|
||||
@@ -60,7 +60,7 @@ export default class ChangeUrlModal extends React.Component {
|
||||
doSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const url = React.findDOMNode(this.refs.urlinput).value;
|
||||
const url = ReactDOM.findDOMNode(this.refs.urlinput).value;
|
||||
const cleanedURL = Utils.cleanUpUrlable(url);
|
||||
if (cleanedURL !== url || url.length < 2 || url.indexOf('__') > -1) {
|
||||
this.setState({urlError: this.getURLError(url)});
|
||||
|
||||
@@ -17,6 +17,9 @@ const AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
|
||||
const Constants = require('../utils/constants.jsx');
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
|
||||
const Popover = ReactBootstrap.Popover;
|
||||
const OverlayTrigger = ReactBootstrap.OverlayTrigger;
|
||||
|
||||
export default class ChannelHeader extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -110,7 +113,21 @@ export default class ChannelHeader extends React.Component {
|
||||
}
|
||||
|
||||
const channel = this.state.channel;
|
||||
const popoverContent = React.renderToString(<MessageWrapper message={channel.description}/>);
|
||||
const popoverContent = (
|
||||
<Popover
|
||||
id='hader-popover'
|
||||
bStyle='info'
|
||||
bSize='large'
|
||||
placement='bottom'
|
||||
className='description'
|
||||
onMouseOver={() => this.refs.descriptionOverlay.show()}
|
||||
onMouseOut={() => this.refs.descriptionOverlay.hide()}
|
||||
>
|
||||
<MessageWrapper
|
||||
message={channel.description}
|
||||
/>
|
||||
</Popover>
|
||||
);
|
||||
let channelTitle = channel.display_name;
|
||||
const currentId = UserStore.getCurrentId();
|
||||
const isAdmin = Utils.isAdmin(this.state.memberChannel.roles) || Utils.isAdmin(this.state.memberTeam.roles);
|
||||
@@ -301,75 +318,82 @@ export default class ChannelHeader extends React.Component {
|
||||
|
||||
return (
|
||||
<table className='channel-header alt'>
|
||||
<tr>
|
||||
<th>
|
||||
<div className='channel-header__info'>
|
||||
<div className='dropdown'>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
<div className='channel-header__info'>
|
||||
<div className='dropdown'>
|
||||
<a
|
||||
href='#'
|
||||
className='dropdown-toggle theme'
|
||||
type='button'
|
||||
id='channel_header_dropdown'
|
||||
data-toggle='dropdown'
|
||||
aria-expanded='true'
|
||||
>
|
||||
<strong className='heading'>{channelTitle} </strong>
|
||||
<span className='glyphicon glyphicon-chevron-down header-dropdown__icon' />
|
||||
</a>
|
||||
<ul
|
||||
className='dropdown-menu'
|
||||
role='menu'
|
||||
aria-labelledby='channel_header_dropdown'
|
||||
>
|
||||
{dropdownContents}
|
||||
</ul>
|
||||
</div>
|
||||
<OverlayTrigger
|
||||
trigger={['hover', 'focus']}
|
||||
placement='bottom'
|
||||
overlay={popoverContent}
|
||||
ref='descriptionOverlay'
|
||||
>
|
||||
<div
|
||||
onClick={TextFormatting.handleClick}
|
||||
className='description'
|
||||
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(channel.description, {singleline: true, mentionHighlight: false})}}
|
||||
/>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<PopoverListMembers
|
||||
members={this.state.users}
|
||||
channelId={channel.id}
|
||||
/>
|
||||
</th>
|
||||
<th className='search-bar__container'><NavbarSearchBox /></th>
|
||||
<th>
|
||||
<div className='dropdown channel-header__links'>
|
||||
<a
|
||||
href='#'
|
||||
className='dropdown-toggle theme'
|
||||
type='button'
|
||||
id='channel_header_dropdown'
|
||||
id='channel_header_right_dropdown'
|
||||
data-toggle='dropdown'
|
||||
aria-expanded='true'
|
||||
>
|
||||
<strong className='heading'>{channelTitle} </strong>
|
||||
<span className='glyphicon glyphicon-chevron-down header-dropdown__icon' />
|
||||
<span dangerouslySetInnerHTML={{__html: Constants.MENU_ICON}} />
|
||||
</a>
|
||||
<ul
|
||||
className='dropdown-menu'
|
||||
className='dropdown-menu dropdown-menu-right'
|
||||
role='menu'
|
||||
aria-labelledby='channel_header_dropdown'
|
||||
aria-labelledby='channel_header_right_dropdown'
|
||||
>
|
||||
{dropdownContents}
|
||||
<li role='presentation'>
|
||||
<a
|
||||
role='menuitem'
|
||||
href='#'
|
||||
onClick={this.searchMentions}
|
||||
>
|
||||
Recent Mentions
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
data-toggle='popover'
|
||||
data-content={popoverContent}
|
||||
className='description'
|
||||
onClick={TextFormatting.handleClick}
|
||||
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(channel.description, {singleline: true, mentionHighlight: false})}}
|
||||
/>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<PopoverListMembers
|
||||
members={this.state.users}
|
||||
channelId={channel.id}
|
||||
/>
|
||||
</th>
|
||||
<th className='search-bar__container'><NavbarSearchBox /></th>
|
||||
<th>
|
||||
<div className='dropdown channel-header__links'>
|
||||
<a
|
||||
href='#'
|
||||
className='dropdown-toggle theme'
|
||||
type='button'
|
||||
id='channel_header_right_dropdown'
|
||||
data-toggle='dropdown'
|
||||
aria-expanded='true'
|
||||
>
|
||||
<span dangerouslySetInnerHTML={{__html: Constants.MENU_ICON}} />
|
||||
</a>
|
||||
<ul
|
||||
className='dropdown-menu dropdown-menu-right'
|
||||
role='menu'
|
||||
aria-labelledby='channel_header_right_dropdown'
|
||||
>
|
||||
<li role='presentation'>
|
||||
<a
|
||||
role='menuitem'
|
||||
href='#'
|
||||
onClick={this.searchMentions}
|
||||
>
|
||||
Recent Mentions
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export default class CommandList extends React.Component {
|
||||
componentDidMount() {
|
||||
var self = this;
|
||||
if (this.refs.modal) {
|
||||
$(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function show(e) {
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', function show(e) {
|
||||
var button = e.relatedTarget;
|
||||
self.setState({channel_id: $(button).attr('data-channelid')});
|
||||
});
|
||||
|
||||
@@ -56,8 +56,8 @@ export default class ChannelInviteModal extends React.Component {
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
$(React.findDOMNode(this)).on('hidden.bs.modal', this.onHide);
|
||||
$(React.findDOMNode(this)).on('show.bs.modal', this.onShow);
|
||||
$(ReactDOM.findDOMNode(this)).on('hidden.bs.modal', this.onHide);
|
||||
$(ReactDOM.findDOMNode(this)).on('show.bs.modal', this.onShow);
|
||||
|
||||
ChannelStore.addExtraInfoChangeListener(this.onListenerChange);
|
||||
ChannelStore.addChangeListener(this.onListenerChange);
|
||||
|
||||
@@ -75,15 +75,6 @@ export default class ChannelLoader extends React.Component {
|
||||
Utils.applyTheme(Constants.THEMES.default);
|
||||
}
|
||||
|
||||
/* Setup global mouse events */
|
||||
$('body').on('click', function hidePopover(e) {
|
||||
$('[data-toggle="popover"]').each(function eachPopover() {
|
||||
if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
|
||||
$(this).popover('hide');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('body').on('mouseenter mouseleave', '.post', function mouseOver(ev) {
|
||||
if (ev.type === 'mouseenter') {
|
||||
$(this).parent('div').prev('.date-separator, .new-separator').addClass('hovered--after');
|
||||
|
||||
@@ -74,9 +74,9 @@ export default class ChannelMembers extends React.Component {
|
||||
componentDidMount() {
|
||||
ChannelStore.addExtraInfoChangeListener(this.onChange);
|
||||
ChannelStore.addChangeListener(this.onChange);
|
||||
$(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.onHide);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.onHide);
|
||||
|
||||
$(React.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
ChannelStore.removeExtraInfoChangeListener(this.onChange);
|
||||
|
||||
@@ -51,7 +51,7 @@ export default class ChannelNotifications extends React.Component {
|
||||
componentDidMount() {
|
||||
ChannelStore.addChangeListener(this.onListenerChange);
|
||||
|
||||
$(React.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
ChannelStore.removeChangeListener(this.onListenerChange);
|
||||
@@ -104,7 +104,7 @@ export default class ChannelNotifications extends React.Component {
|
||||
}
|
||||
handleUpdateNotifyLevel(notifyLevel) {
|
||||
this.setState({notifyLevel});
|
||||
React.findDOMNode(this.refs.modal).focus();
|
||||
ReactDOM.findDOMNode(this.refs.modal).focus();
|
||||
}
|
||||
createNotifyLevelSection(serverError) {
|
||||
var handleUpdateSection;
|
||||
@@ -266,7 +266,7 @@ export default class ChannelNotifications extends React.Component {
|
||||
|
||||
handleUpdateMarkUnreadLevel(markUnreadLevel) {
|
||||
this.setState({markUnreadLevel});
|
||||
React.findDOMNode(this.refs.modal).focus();
|
||||
ReactDOM.findDOMNode(this.refs.modal).focus();
|
||||
}
|
||||
|
||||
createMarkUnreadLevelSection(serverError) {
|
||||
|
||||
@@ -46,7 +46,9 @@ export default class CreateComment extends React.Component {
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevState.uploadsInProgress < this.state.uploadsInProgress) {
|
||||
$('.post-right__scroll').scrollTop($('.post-right__scroll')[0].scrollHeight);
|
||||
$('.post-right__scroll').perfectScrollbar('update');
|
||||
if ($(window).width() > 768) {
|
||||
$('.post-right__scroll').perfectScrollbar('update');
|
||||
}
|
||||
}
|
||||
}
|
||||
handleSubmit(e) {
|
||||
@@ -126,7 +128,7 @@ export default class CreateComment extends React.Component {
|
||||
commentMsgKeyPress(e) {
|
||||
if (e.which === 13 && !e.shiftKey && !e.altKey) {
|
||||
e.preventDefault();
|
||||
React.findDOMNode(this.refs.textbox).blur();
|
||||
ReactDOM.findDOMNode(this.refs.textbox).blur();
|
||||
this.handleSubmit(e);
|
||||
}
|
||||
|
||||
@@ -189,7 +191,7 @@ export default class CreateComment extends React.Component {
|
||||
handleTextDrop(text) {
|
||||
const newText = this.state.messageText + text;
|
||||
this.handleUserInput(newText);
|
||||
Utils.setCaretPosition(React.findDOMNode(this.refs.textbox.refs.message), newText.length);
|
||||
Utils.setCaretPosition(ReactDOM.findDOMNode(this.refs.textbox.refs.message), newText.length);
|
||||
}
|
||||
removePreview(id) {
|
||||
let previews = this.state.previews;
|
||||
@@ -255,6 +257,17 @@ export default class CreateComment extends React.Component {
|
||||
postFooterClassName += ' has-error';
|
||||
}
|
||||
|
||||
let uploadsInProgressText = null;
|
||||
if (this.state.uploadsInProgress.length > 0) {
|
||||
uploadsInProgressText = (
|
||||
<span
|
||||
className='pull-right post-right-comments-upload-in-progress'
|
||||
>
|
||||
{this.state.uploadsInProgress.length === 1 ? 'File uploading' : 'Files uploading'}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div className='post-create'>
|
||||
@@ -295,6 +308,7 @@ export default class CreateComment extends React.Component {
|
||||
value='Add Comment'
|
||||
onClick={this.handleSubmit}
|
||||
/>
|
||||
{uploadsInProgressText}
|
||||
{postError}
|
||||
{serverError}
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,7 @@ const Utils = require('../utils/utils.jsx');
|
||||
|
||||
const Constants = require('../utils/constants.jsx');
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
const KeyCodes = Constants.KeyCodes;
|
||||
|
||||
export default class CreatePost extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -35,6 +36,7 @@ export default class CreatePost extends React.Component {
|
||||
this.removePreview = this.removePreview.bind(this);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.getFileCount = this.getFileCount.bind(this);
|
||||
this.handleArrowUp = this.handleArrowUp.bind(this);
|
||||
|
||||
PostStore.clearDraftUploads();
|
||||
|
||||
@@ -172,9 +174,9 @@ export default class CreatePost extends React.Component {
|
||||
}
|
||||
}
|
||||
postMsgKeyPress(e) {
|
||||
if (e.which === 13 && !e.shiftKey && !e.altKey) {
|
||||
if (e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) {
|
||||
e.preventDefault();
|
||||
React.findDOMNode(this.refs.textbox).blur();
|
||||
ReactDOM.findDOMNode(this.refs.textbox).blur();
|
||||
this.handleSubmit(e);
|
||||
}
|
||||
|
||||
@@ -192,7 +194,7 @@ export default class CreatePost extends React.Component {
|
||||
PostStore.storeCurrentDraft(draft);
|
||||
}
|
||||
resizePostHolder() {
|
||||
const height = $(window).height() - $(React.findDOMNode(this.refs.topDiv)).height() - 50;
|
||||
const height = $(window).height() - $(ReactDOM.findDOMNode(this.refs.topDiv)).height() - 50;
|
||||
$('.post-list-holder-by-time').css('height', `${height}px`);
|
||||
$(window).trigger('resize');
|
||||
if ($(window).width() > 960) {
|
||||
@@ -243,7 +245,7 @@ export default class CreatePost extends React.Component {
|
||||
handleTextDrop(text) {
|
||||
const newText = this.state.messageText + text;
|
||||
this.handleUserInput(newText);
|
||||
Utils.setCaretPosition(React.findDOMNode(this.refs.textbox.refs.message), newText.length);
|
||||
Utils.setCaretPosition(ReactDOM.findDOMNode(this.refs.textbox.refs.message), newText.length);
|
||||
}
|
||||
removePreview(id) {
|
||||
const previews = Object.assign([], this.state.previews);
|
||||
@@ -292,6 +294,27 @@ export default class CreatePost extends React.Component {
|
||||
const draft = PostStore.getDraft(channelId);
|
||||
return draft.previews.length + draft.uploadsInProgress.length;
|
||||
}
|
||||
handleArrowUp(e) {
|
||||
if (e.keyCode === KeyCodes.UP && this.state.messageText === '') {
|
||||
e.preventDefault();
|
||||
|
||||
const channelId = ChannelStore.getCurrentId();
|
||||
const lastPost = PostStore.getCurrentUsersLatestPost(channelId);
|
||||
if (!lastPost) {
|
||||
return;
|
||||
}
|
||||
var type = (lastPost.root_id && lastPost.root_id.length > 0) ? 'Comment' : 'Post';
|
||||
|
||||
AppDispatcher.handleViewAction({
|
||||
type: ActionTypes.RECIEVED_EDIT_POST,
|
||||
refocusId: '#post_textbox',
|
||||
title: type,
|
||||
message: lastPost.message,
|
||||
postId: lastPost.id,
|
||||
channelId: lastPost.channel_id
|
||||
});
|
||||
}
|
||||
}
|
||||
render() {
|
||||
let serverError = null;
|
||||
if (this.state.serverError) {
|
||||
@@ -336,6 +359,7 @@ export default class CreatePost extends React.Component {
|
||||
<Textbox
|
||||
onUserInput={this.handleUserInput}
|
||||
onKeyPress={this.postMsgKeyPress}
|
||||
onKeyDown={this.handleArrowUp}
|
||||
onHeightChange={this.resizePostHolder}
|
||||
messageText={this.state.messageText}
|
||||
createMessage='Write a message...'
|
||||
|
||||
@@ -41,7 +41,7 @@ export default class DeleteChannelModal extends React.Component {
|
||||
});
|
||||
}
|
||||
componentDidMount() {
|
||||
$(React.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
|
||||
}
|
||||
render() {
|
||||
const channel = ChannelStore.getCurrent();
|
||||
|
||||
@@ -73,7 +73,7 @@ export default class DeletePostModal extends React.Component {
|
||||
this.setState(newState);
|
||||
}
|
||||
componentDidMount() {
|
||||
$(React.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
|
||||
PostStore.addSelectedPostChangeListener(this.onListenerChange);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
|
||||
@@ -34,7 +34,7 @@ export default class EditChannelModal extends React.Component {
|
||||
function handleUpdateSuccess() {
|
||||
this.setState({serverError: ''});
|
||||
AsyncClient.getChannel(this.state.channelId);
|
||||
$(React.findDOMNode(this.refs.modal)).modal('hide');
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).modal('hide');
|
||||
}.bind(this),
|
||||
function handleUpdateError(err) {
|
||||
if (err.message === 'Invalid channel_description parameter') {
|
||||
@@ -56,11 +56,11 @@ export default class EditChannelModal extends React.Component {
|
||||
this.setState({description: $(button).attr('data-desc'), title: $(button).attr('data-title'), channelId: $(button).attr('data-channelid'), serverError: ''});
|
||||
}
|
||||
componentDidMount() {
|
||||
$(React.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
|
||||
$(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.handleClose);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.handleClose);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
$(React.findDOMNode(this.refs.modal)).off('hidden.bs.modal', this.handleClose);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).off('hidden.bs.modal', this.handleClose);
|
||||
}
|
||||
render() {
|
||||
var serverError = null;
|
||||
|
||||
@@ -5,6 +5,7 @@ var Client = require('../utils/client.jsx');
|
||||
var AsyncClient = require('../utils/async_client.jsx');
|
||||
var Textbox = require('./textbox.jsx');
|
||||
var BrowserStore = require('../stores/browser_store.jsx');
|
||||
var PostStore = require('../stores/post_store.jsx');
|
||||
|
||||
export default class EditPostModal extends React.Component {
|
||||
constructor() {
|
||||
@@ -14,6 +15,7 @@ export default class EditPostModal extends React.Component {
|
||||
this.handleEditInput = this.handleEditInput.bind(this);
|
||||
this.handleEditKeyPress = this.handleEditKeyPress.bind(this);
|
||||
this.handleUserInput = this.handleUserInput.bind(this);
|
||||
this.handleEditPostEvent = this.handleEditPostEvent.bind(this);
|
||||
|
||||
this.state = {editText: '', title: '', post_id: '', channel_id: '', comments: 0, refocusId: ''};
|
||||
}
|
||||
@@ -35,16 +37,15 @@ export default class EditPostModal extends React.Component {
|
||||
|
||||
Client.updatePost(updatedPost,
|
||||
function success() {
|
||||
AsyncClient.getPosts(this.state.channel_id);
|
||||
AsyncClient.getPosts(updatedPost.channel_id);
|
||||
window.scrollTo(0, 0);
|
||||
}.bind(this),
|
||||
},
|
||||
function error(err) {
|
||||
AsyncClient.dispatchError(err, 'updatePost');
|
||||
}
|
||||
);
|
||||
|
||||
$('#edit_post').modal('hide');
|
||||
$(this.state.refocusId).focus();
|
||||
}
|
||||
handleEditInput(editMessage) {
|
||||
this.setState({editText: editMessage});
|
||||
@@ -52,28 +53,57 @@ export default class EditPostModal extends React.Component {
|
||||
handleEditKeyPress(e) {
|
||||
if (e.which === 13 && !e.shiftKey && !e.altKey) {
|
||||
e.preventDefault();
|
||||
React.findDOMNode(this.refs.editbox).blur();
|
||||
ReactDOM.findDOMNode(this.refs.editbox).blur();
|
||||
this.handleEdit(e);
|
||||
}
|
||||
}
|
||||
handleUserInput(e) {
|
||||
this.setState({editText: e.target.value});
|
||||
}
|
||||
handleEditPostEvent(options) {
|
||||
this.setState({
|
||||
editText: options.message || '',
|
||||
title: options.title || '',
|
||||
post_id: options.postId || '',
|
||||
channel_id: options.channelId || '',
|
||||
comments: options.comments || 0,
|
||||
refocusId: options.refocusId || ''
|
||||
});
|
||||
|
||||
$(React.findDOMNode(this.refs.modal)).modal('show');
|
||||
}
|
||||
componentDidMount() {
|
||||
var self = this;
|
||||
|
||||
$(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function onHidden() {
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function onHidden() {
|
||||
self.setState({editText: '', title: '', channel_id: '', post_id: '', comments: 0, refocusId: '', error: ''});
|
||||
});
|
||||
|
||||
$(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function onShow(e) {
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', function onShow(e) {
|
||||
var button = e.relatedTarget;
|
||||
if (!button) {
|
||||
return;
|
||||
}
|
||||
self.setState({editText: $(button).attr('data-message'), title: $(button).attr('data-title'), channel_id: $(button).attr('data-channelid'), post_id: $(button).attr('data-postid'), comments: $(button).attr('data-comments'), refocusId: $(button).attr('data-refoucsid')});
|
||||
});
|
||||
|
||||
$(React.findDOMNode(this.refs.modal)).on('shown.bs.modal', function onShown() {
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', function onShown() {
|
||||
self.refs.editbox.resize();
|
||||
$('#edit_textbox').get(0).focus();
|
||||
});
|
||||
|
||||
$(React.findDOMNode(this.refs.modal)).on('hide.bs.modal', function onShown() {
|
||||
if (self.state.refocusId !== '') {
|
||||
setTimeout(() => {
|
||||
$(self.state.refocusId).get(0).focus();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
PostStore.addEditPostListener(this.handleEditPostEvent);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
PostStore.removeEditPostListener(this.handleEditPostEvent);
|
||||
}
|
||||
render() {
|
||||
var error = (<div className='form-group'><br /></div>);
|
||||
|
||||
@@ -39,11 +39,9 @@ export default class EmailVerify extends React.Component {
|
||||
|
||||
return (
|
||||
<div className='col-sm-12'>
|
||||
<div className='panel panel-default verify_panel'>
|
||||
<div className='panel-heading'>
|
||||
<h3 className='panel-title'>{title}</h3>
|
||||
</div>
|
||||
<div className='panel-body'>
|
||||
<div className='signup-team__container'>
|
||||
<h3>{title}</h3>
|
||||
<div>
|
||||
{body}
|
||||
{resend}
|
||||
{resendConfirm}
|
||||
|
||||
@@ -43,7 +43,7 @@ export default class FileAttachment extends React.Component {
|
||||
return function loader() {
|
||||
$(this).remove();
|
||||
if (name in self.refs) {
|
||||
var imgDiv = React.findDOMNode(self.refs[name]);
|
||||
var imgDiv = ReactDOM.findDOMNode(self.refs[name]);
|
||||
|
||||
$(imgDiv).removeClass('post__load');
|
||||
$(imgDiv).addClass('post__image');
|
||||
@@ -82,7 +82,7 @@ export default class FileAttachment extends React.Component {
|
||||
if (nextState.fileSize !== this.state.fileSize) {
|
||||
if (this.refs.fileSize) {
|
||||
// update the UI element to display the file size without re-rendering the whole component
|
||||
React.findDOMNode(this.refs.fileSize).innerHTML = utils.fileSizeToString(nextState.fileSize);
|
||||
ReactDOM.findDOMNode(this.refs.fileSize).innerHTML = utils.fileSizeToString(nextState.fileSize);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ export default class FileUpload extends React.Component {
|
||||
}
|
||||
|
||||
handleChange() {
|
||||
var element = $(React.findDOMNode(this.refs.fileInput));
|
||||
var element = $(ReactDOM.findDOMNode(this.refs.fileInput));
|
||||
|
||||
this.uploadFiles(element.prop('files'));
|
||||
|
||||
@@ -115,7 +115,7 @@ export default class FileUpload extends React.Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
var inputDiv = React.findDOMNode(this.refs.input);
|
||||
var inputDiv = ReactDOM.findDOMNode(this.refs.input);
|
||||
var self = this;
|
||||
|
||||
if (this.props.postType === 'post') {
|
||||
|
||||
@@ -17,7 +17,7 @@ export default class FindTeam extends React.Component {
|
||||
|
||||
var state = { };
|
||||
|
||||
var email = React.findDOMNode(this.refs.email).value.trim().toLowerCase();
|
||||
var email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
|
||||
if (!email || !utils.isEmail(email)) {
|
||||
state.email_error = 'Please enter a valid email address';
|
||||
this.setState(state);
|
||||
|
||||
@@ -22,12 +22,12 @@ export default class GetLinkModal extends React.Component {
|
||||
}
|
||||
componentDidMount() {
|
||||
if (this.refs.modal) {
|
||||
$(React.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
|
||||
$(React.findDOMNode(this.refs.modal)).on('hide.bs.modal', this.onHide);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', this.onShow);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('hide.bs.modal', this.onHide);
|
||||
}
|
||||
}
|
||||
handleClick() {
|
||||
var copyTextarea = $(React.findDOMNode(this.refs.textarea));
|
||||
var copyTextarea = $(ReactDOM.findDOMNode(this.refs.textarea));
|
||||
copyTextarea.select();
|
||||
|
||||
try {
|
||||
|
||||
@@ -36,7 +36,7 @@ export default class InviteMemberModal extends React.Component {
|
||||
var notEmpty = false;
|
||||
for (var i = 0; i < self.state.inviteIds.length; i++) {
|
||||
var index = self.state.inviteIds[i];
|
||||
if (React.findDOMNode(self.refs['email' + index]).value.trim() !== '') {
|
||||
if (ReactDOM.findDOMNode(self.refs['email' + index]).value.trim() !== '') {
|
||||
notEmpty = true;
|
||||
break;
|
||||
}
|
||||
@@ -69,7 +69,7 @@ export default class InviteMemberModal extends React.Component {
|
||||
for (var i = 0; i < count; i++) {
|
||||
var index = inviteIds[i];
|
||||
var invite = {};
|
||||
invite.email = React.findDOMNode(this.refs['email' + index]).value.trim();
|
||||
invite.email = ReactDOM.findDOMNode(this.refs['email' + index]).value.trim();
|
||||
if (!invite.email || !utils.isEmail(invite.email)) {
|
||||
emailErrors[index] = 'Please enter a valid email address';
|
||||
valid = false;
|
||||
@@ -77,9 +77,9 @@ export default class InviteMemberModal extends React.Component {
|
||||
emailErrors[index] = '';
|
||||
}
|
||||
|
||||
invite.firstName = React.findDOMNode(this.refs['first_name' + index]).value.trim();
|
||||
invite.firstName = ReactDOM.findDOMNode(this.refs['first_name' + index]).value.trim();
|
||||
|
||||
invite.lastName = React.findDOMNode(this.refs['last_name' + index]).value.trim();
|
||||
invite.lastName = ReactDOM.findDOMNode(this.refs['last_name' + index]).value.trim();
|
||||
|
||||
invites.push(invite);
|
||||
}
|
||||
@@ -95,8 +95,8 @@ export default class InviteMemberModal extends React.Component {
|
||||
|
||||
Client.inviteMembers(data,
|
||||
function success() {
|
||||
$(React.findDOMNode(this.refs.modal)).attr('data-confirm', 'true');
|
||||
$(React.findDOMNode(this.refs.modal)).modal('hide');
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).attr('data-confirm', 'true');
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).modal('hide');
|
||||
}.bind(this),
|
||||
function fail(err) {
|
||||
if (err.message === 'This person is already on your team') {
|
||||
@@ -110,8 +110,8 @@ export default class InviteMemberModal extends React.Component {
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
$(React.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
|
||||
$(React.findDOMNode(this.refs.modalBody)).css('overflow-y', 'scroll');
|
||||
$(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
|
||||
$(ReactDOM.findDOMNode(this.refs.modalBody)).css('overflow-y', 'scroll');
|
||||
}
|
||||
|
||||
addInviteFields() {
|
||||
@@ -126,9 +126,9 @@ export default class InviteMemberModal extends React.Component {
|
||||
|
||||
for (var i = 0; i < inviteIds.length; i++) {
|
||||
var index = inviteIds[i];
|
||||
React.findDOMNode(this.refs['email' + index]).value = '';
|
||||
React.findDOMNode(this.refs['first_name' + index]).value = '';
|
||||
React.findDOMNode(this.refs['last_name' + index]).value = '';
|
||||
ReactDOM.findDOMNode(this.refs['email' + index]).value = '';
|
||||
ReactDOM.findDOMNode(this.refs['first_name' + index]).value = '';
|
||||
ReactDOM.findDOMNode(this.refs['last_name' + index]).value = '';
|
||||
}
|
||||
|
||||
this.setState({
|
||||
|
||||
@@ -25,14 +25,14 @@ export default class Login extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const email = React.findDOMNode(this.refs.email).value.trim();
|
||||
const email = ReactDOM.findDOMNode(this.refs.email).value.trim();
|
||||
if (!email) {
|
||||
state.serverError = 'An email is required';
|
||||
this.setState(state);
|
||||
return;
|
||||
}
|
||||
|
||||
const password = React.findDOMNode(this.refs.password).value.trim();
|
||||
const password = ReactDOM.findDOMNode(this.refs.password).value.trim();
|
||||
if (!password) {
|
||||
state.serverError = 'A password is required';
|
||||
this.setState(state);
|
||||
|
||||
@@ -61,7 +61,7 @@ export default class MentionList extends React.Component {
|
||||
}
|
||||
onClick(e) {
|
||||
if (!($('#' + this.props.id).is(e.target) || $('#' + this.props.id).has(e.target).length ||
|
||||
('mentionlist' in this.refs && $(React.findDOMNode(this.refs.mentionlist)).has(e.target).length))) {
|
||||
('mentionlist' in this.refs && $(ReactDOM.findDOMNode(this.refs.mentionlist)).has(e.target).length))) {
|
||||
this.setState({mentionText: '-1'});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,12 +31,12 @@ export default class MoreChannels extends React.Component {
|
||||
}
|
||||
componentDidMount() {
|
||||
ChannelStore.addMoreChangeListener(this.onListenerChange);
|
||||
$(React.findDOMNode(this.refs.modal)).on('shown.bs.modal', function shown() {
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', function shown() {
|
||||
asyncClient.getMoreChannels(true);
|
||||
});
|
||||
|
||||
var self = this;
|
||||
$(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function show(e) {
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', function show(e) {
|
||||
var button = e.relatedTarget;
|
||||
self.setState({channelType: $(button).attr('data-channeltype')});
|
||||
});
|
||||
@@ -54,7 +54,7 @@ export default class MoreChannels extends React.Component {
|
||||
this.setState({joiningChannel: channelIndex});
|
||||
client.joinChannel(channel.id,
|
||||
function joinSuccess() {
|
||||
$(React.findDOMNode(this.refs.modal)).modal('hide');
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).modal('hide');
|
||||
asyncClient.getChannel(channel.id);
|
||||
utils.switchChannel(channel);
|
||||
this.setState({joiningChannel: -1});
|
||||
@@ -65,7 +65,7 @@ export default class MoreChannels extends React.Component {
|
||||
);
|
||||
}
|
||||
handleNewChannel() {
|
||||
$(React.findDOMNode(this.refs.modal)).modal('hide');
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).modal('hide');
|
||||
this.setState({showNewChannelModal: true});
|
||||
}
|
||||
render() {
|
||||
|
||||
@@ -1,133 +1,273 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
var TeamStore = require('../stores/team_store.jsx');
|
||||
var Client = require('../utils/client.jsx');
|
||||
var Constants = require('../utils/constants.jsx');
|
||||
var AsyncClient = require('../utils/async_client.jsx');
|
||||
var PreferenceStore = require('../stores/preference_store.jsx');
|
||||
var utils = require('../utils/utils.jsx');
|
||||
const AsyncClient = require('../utils/async_client.jsx');
|
||||
const ChannelStore = require('../stores/channel_store.jsx');
|
||||
const Constants = require('../utils/constants.jsx');
|
||||
const Client = require('../utils/client.jsx');
|
||||
const Modal = ReactBootstrap.Modal;
|
||||
const PreferenceStore = require('../stores/preference_store.jsx');
|
||||
const TeamStore = require('../stores/team_store.jsx');
|
||||
const UserStore = require('../stores/user_store.jsx');
|
||||
const Utils = require('../utils/utils.jsx');
|
||||
|
||||
export default class MoreDirectChannels extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {channels: [], loadingDMChannel: -1};
|
||||
this.handleFilterChange = this.handleFilterChange.bind(this);
|
||||
this.handleHide = this.handleHide.bind(this);
|
||||
this.handleShowDirectChannel = this.handleShowDirectChannel.bind(this);
|
||||
this.handleUserChange = this.handleUserChange.bind(this);
|
||||
|
||||
this.createRowForUser = this.createRowForUser.bind(this);
|
||||
|
||||
this.state = {
|
||||
users: this.getUsersFromStore(),
|
||||
filter: '',
|
||||
loadingDMChannel: -1
|
||||
};
|
||||
}
|
||||
|
||||
getUsersFromStore() {
|
||||
const currentId = UserStore.getCurrentId();
|
||||
const profiles = UserStore.getProfiles();
|
||||
const users = [];
|
||||
|
||||
for (const id in profiles) {
|
||||
if (id !== currentId) {
|
||||
users.push(profiles[id]);
|
||||
}
|
||||
}
|
||||
|
||||
users.sort((a, b) => a.username.localeCompare(b.username));
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
$(React.findDOMNode(this.refs.modal)).on('show.bs.modal', (e) => {
|
||||
var button = e.relatedTarget;
|
||||
this.setState({channels: $(button).data('channels')}); // eslint-disable-line react/no-did-mount-set-state
|
||||
});
|
||||
UserStore.addChangeListener(this.handleUserChange);
|
||||
}
|
||||
|
||||
handleJoinDirectChannel(channel) {
|
||||
const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, channel.teammate_id, 'true');
|
||||
componentWillUnmount() {
|
||||
UserStore.addChangeListener(this.handleUserChange);
|
||||
}
|
||||
|
||||
handleFilterChange() {
|
||||
const filter = ReactDOM.findDOMNode(this.refs.filter).value;
|
||||
|
||||
if (filter !== this.state.filter) {
|
||||
this.setState({filter});
|
||||
}
|
||||
}
|
||||
|
||||
handleHide() {
|
||||
if (this.props.onModalDismissed) {
|
||||
this.props.onModalDismissed();
|
||||
}
|
||||
|
||||
this.setState({filter: ''});
|
||||
}
|
||||
|
||||
handleShowDirectChannel(teammate, e) {
|
||||
if (this.state.loadingDMChannel !== -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const channelName = Utils.getDirectChannelName(UserStore.getCurrentId(), teammate.id);
|
||||
let channel = ChannelStore.getByName(channelName);
|
||||
|
||||
const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, teammate.id, 'true');
|
||||
AsyncClient.savePreferences([preference]);
|
||||
|
||||
if (channel) {
|
||||
Utils.switchChannel(channel);
|
||||
|
||||
this.handleHide();
|
||||
} else {
|
||||
this.setState({loadingDMChannel: teammate.id});
|
||||
|
||||
channel = {
|
||||
name: channelName,
|
||||
last_post_at: 0,
|
||||
total_msg_count: 0,
|
||||
type: 'D',
|
||||
display_name: teammate.username,
|
||||
teammate_id: teammate.id,
|
||||
status: UserStore.getStatus(teammate.id)
|
||||
};
|
||||
|
||||
Client.createDirectChannel(
|
||||
channel,
|
||||
teammate.id,
|
||||
(data) => {
|
||||
this.setState({loadingDMChannel: -1});
|
||||
|
||||
AsyncClient.getChannel(data.id);
|
||||
Utils.switchChannel(data);
|
||||
|
||||
this.handleHide();
|
||||
},
|
||||
() => {
|
||||
this.setState({loadingDMChannel: -1});
|
||||
window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channelName;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
handleUserChange() {
|
||||
this.setState({users: this.getUsersFromStore()});
|
||||
}
|
||||
|
||||
createRowForUser(user) {
|
||||
const details = [];
|
||||
|
||||
const fullName = Utils.getFullName(user);
|
||||
if (fullName) {
|
||||
details.push(
|
||||
<span
|
||||
key={`${user.id}__full-name`}
|
||||
className='full-name'
|
||||
>
|
||||
{fullName}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
if (user.nickname) {
|
||||
const separator = fullName ? ' - ' : '';
|
||||
details.push(
|
||||
<span
|
||||
key={`${user.nickname}__nickname`}
|
||||
className='nickname'
|
||||
>
|
||||
{separator + user.nickname}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
let joinButton;
|
||||
if (this.state.loadingDMChannel === user.id) {
|
||||
joinButton = (
|
||||
<img
|
||||
className='channel-loading-gif'
|
||||
src='/static/images/load.gif'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
joinButton = (
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-primary btn-message'
|
||||
onClick={this.handleShowDirectChannel.bind(this, user)}
|
||||
>
|
||||
{'Message'}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<li
|
||||
key={user.id}
|
||||
className='direct-channel'
|
||||
>
|
||||
<div className='col-xs-1 image-div'>
|
||||
<img
|
||||
className='profile-image'
|
||||
src={`/api/v1/users/${user.id}/image?time=${user.update_at}`}
|
||||
/>
|
||||
</div>
|
||||
<div className='col-xs-9'>
|
||||
<div className='username'>
|
||||
{user.username}
|
||||
</div>
|
||||
<div>
|
||||
{details}
|
||||
</div>
|
||||
</div>
|
||||
<div className='col-xs-2 btn-div'>
|
||||
{joinButton}
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
var directMessageItems = this.state.channels.map((channel, index) => {
|
||||
var badge = '';
|
||||
var titleClass = '';
|
||||
var handleClick = null;
|
||||
if (!this.props.show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (channel.fake) {
|
||||
// It's a direct message channel that doesn't exist yet so let's create it now
|
||||
var otherUserId = utils.getUserIdFromChannelName(channel);
|
||||
let users = this.state.users;
|
||||
if (this.state.filter !== '') {
|
||||
users = users.filter((user) => {
|
||||
return user.username.indexOf(this.state.filter) !== -1 ||
|
||||
user.first_name.indexOf(this.state.filter) !== -1 ||
|
||||
user.last_name.indexOf(this.state.filter) !== -1 ||
|
||||
user.nickname.indexOf(this.state.filter) !== -1;
|
||||
});
|
||||
}
|
||||
|
||||
if (this.state.loadingDMChannel === index) {
|
||||
badge = (
|
||||
<img
|
||||
className='channel-loading-gif pull-right'
|
||||
src='/static/images/load.gif'
|
||||
/>
|
||||
);
|
||||
}
|
||||
const userEntries = users.map(this.createRowForUser);
|
||||
|
||||
if (this.state.loadingDMChannel === -1) {
|
||||
handleClick = (e) => {
|
||||
e.preventDefault();
|
||||
this.setState({loadingDMChannel: index});
|
||||
this.handleJoinDirectChannel(channel);
|
||||
if (userEntries.length === 0) {
|
||||
userEntries.push(<li key='no-users-found'>{'No users found :('}</li>);
|
||||
}
|
||||
|
||||
Client.createDirectChannel(channel, otherUserId,
|
||||
(data) => {
|
||||
$(React.findDOMNode(this.refs.modal)).modal('hide');
|
||||
this.setState({loadingDMChannel: -1});
|
||||
AsyncClient.getChannel(data.id);
|
||||
utils.switchChannel(data);
|
||||
},
|
||||
() => {
|
||||
this.setState({loadingDMChannel: -1});
|
||||
window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name;
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (channel.unread) {
|
||||
badge = <span className='badge pull-right small'>{channel.unread}</span>;
|
||||
titleClass = 'unread-title';
|
||||
}
|
||||
let memberString = 'Member';
|
||||
if (users.length !== 1) {
|
||||
memberString += 's';
|
||||
}
|
||||
|
||||
handleClick = (e) => {
|
||||
e.preventDefault();
|
||||
this.handleJoinDirectChannel(channel);
|
||||
utils.switchChannel(channel);
|
||||
$(React.findDOMNode(this.refs.modal)).modal('hide');
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<li key={channel.name}>
|
||||
<a
|
||||
className={'sidebar-channel ' + titleClass}
|
||||
href='#'
|
||||
onClick={handleClick}
|
||||
>{badge}{channel.display_name}</a>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
let count;
|
||||
if (users.length === this.state.users.length) {
|
||||
count = `${users.length} ${memberString}`;
|
||||
} else {
|
||||
count = `${users.length} ${memberString} of ${this.state.users.length} Total`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className='modal fade'
|
||||
id='more_direct_channels'
|
||||
ref='modal'
|
||||
tabIndex='-1'
|
||||
role='dialog'
|
||||
aria-hidden='true'
|
||||
<Modal
|
||||
className='modal-direct-channels'
|
||||
show={this.props.show}
|
||||
bsSize='large'
|
||||
onHide={this.handleHide}
|
||||
>
|
||||
<div className='modal-dialog'>
|
||||
<div className='modal-content'>
|
||||
<div className='modal-header'>
|
||||
<button
|
||||
type='button'
|
||||
className='close'
|
||||
data-dismiss='modal'
|
||||
>
|
||||
<span aria-hidden='true'>{'×'}</span>
|
||||
<span className='sr-only'>{'Close'}</span>
|
||||
</button>
|
||||
<h4 className='modal-title'>{'More Direct Messages'}</h4>
|
||||
</div>
|
||||
<div className='modal-body'>
|
||||
<ul className='nav nav-pills nav-stacked'>
|
||||
{directMessageItems}
|
||||
</ul>
|
||||
</div>
|
||||
<div className='modal-footer'>
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-default'
|
||||
data-dismiss='modal'
|
||||
>{'Close'}</button>
|
||||
</div>
|
||||
<Modal.Header closeButton={true}>
|
||||
<Modal.Title>{'More Direct Messages'}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<div>
|
||||
<input
|
||||
ref='filter'
|
||||
className='form-control filter-textbox'
|
||||
placeholder='Search members'
|
||||
onInput={this.handleFilterChange}
|
||||
style={{width: '200px', display: 'inline'}}
|
||||
/>
|
||||
<span className='member-count pull-right'>{count}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul className='user-list'>
|
||||
{userEntries}
|
||||
</ul>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-default'
|
||||
onClick={this.handleHide}
|
||||
>
|
||||
{'Close'}
|
||||
</button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MoreDirectChannels.propTypes = {
|
||||
show: React.PropTypes.bool.isRequired,
|
||||
onModalDismissed: React.PropTypes.func
|
||||
};
|
||||
|
||||
@@ -14,6 +14,9 @@ var Constants = require('../utils/constants.jsx');
|
||||
var ActionTypes = Constants.ActionTypes;
|
||||
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
|
||||
|
||||
var Popover = ReactBootstrap.Popover;
|
||||
var OverlayTrigger = ReactBootstrap.OverlayTrigger;
|
||||
|
||||
export default class Navbar extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -36,12 +39,6 @@ export default class Navbar extends React.Component {
|
||||
ChannelStore.addChangeListener(this.onChange);
|
||||
ChannelStore.addExtraInfoChangeListener(this.onChange);
|
||||
$('.inner__wrap').click(this.hideSidebars);
|
||||
|
||||
$('body').on('click.infopopover', function handlePopoverClick(e) {
|
||||
if ($(e.target).attr('data-toggle') !== 'popover' && $(e.target).parents('.popover.in').length === 0) {
|
||||
$('.info-popover').popover('hide');
|
||||
}
|
||||
});
|
||||
}
|
||||
componentWillUnmount() {
|
||||
ChannelStore.removeChangeListener(this.onChange);
|
||||
@@ -224,11 +221,15 @@ export default class Navbar extends React.Component {
|
||||
return (
|
||||
<div className='navbar-brand'>
|
||||
<div className='dropdown'>
|
||||
<div
|
||||
data-toggle='popover'
|
||||
data-content={popoverContent}
|
||||
className='description info-popover'
|
||||
/>
|
||||
<OverlayTrigger
|
||||
trigger='click'
|
||||
placement='bottom'
|
||||
overlay={popoverContent}
|
||||
className='description'
|
||||
rootClose={true}
|
||||
>
|
||||
<div className='description info-popover'/>
|
||||
</OverlayTrigger>
|
||||
<a
|
||||
href='#'
|
||||
className='dropdown-toggle theme'
|
||||
@@ -330,11 +331,17 @@ export default class Navbar extends React.Component {
|
||||
var isDirect = false;
|
||||
|
||||
if (channel) {
|
||||
popoverContent = React.renderToString(
|
||||
<MessageWrapper
|
||||
message={channel.description}
|
||||
options={{singleline: true, mentionHighlight: false}}
|
||||
/>
|
||||
popoverContent = (
|
||||
<Popover
|
||||
bsStyle='info'
|
||||
placement='bottom'
|
||||
id='description-popover'
|
||||
>
|
||||
<MessageWrapper
|
||||
message={channel.description}
|
||||
options={{singleline: true, mentionHighlight: false}}
|
||||
/>
|
||||
</Popover>
|
||||
);
|
||||
isAdmin = Utils.isAdmin(this.state.member.roles);
|
||||
|
||||
@@ -354,19 +361,28 @@ export default class Navbar extends React.Component {
|
||||
}
|
||||
|
||||
if (channel.description.length === 0) {
|
||||
popoverContent = React.renderToString(
|
||||
<div>
|
||||
No channel description yet. <br/>
|
||||
<a
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
data-desc={channel.description}
|
||||
data-title={channel.display_name}
|
||||
data-channelid={channel.id}
|
||||
data-target='#edit_channel'
|
||||
>
|
||||
Click here
|
||||
</a> to add one.</div>
|
||||
popoverContent = (
|
||||
<Popover
|
||||
bsStyle='info'
|
||||
placement='bottom'
|
||||
id='description-popover'
|
||||
>
|
||||
<div>
|
||||
{'No channel description yet.'}
|
||||
<br/>
|
||||
<a
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
data-desc={channel.description}
|
||||
data-title={channel.display_name}
|
||||
data-channelid={channel.id}
|
||||
data-target='#edit_channel'
|
||||
>
|
||||
{'Click here'}
|
||||
</a>
|
||||
{' to add one.'}
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ export default class NavbarDropdown extends React.Component {
|
||||
UserStore.addTeamsChangeListener(this.onListenerChange);
|
||||
TeamStore.addChangeListener(this.onListenerChange);
|
||||
|
||||
$(React.findDOMNode(this.refs.dropdown)).on('hide.bs.dropdown', () => {
|
||||
$(ReactDOM.findDOMNode(this.refs.dropdown)).on('hide.bs.dropdown', () => {
|
||||
this.blockToggle = true;
|
||||
setTimeout(() => {
|
||||
this.blockToggle = false;
|
||||
@@ -51,7 +51,7 @@ export default class NavbarDropdown extends React.Component {
|
||||
UserStore.removeTeamsChangeListener(this.onListenerChange);
|
||||
TeamStore.removeChangeListener(this.onListenerChange);
|
||||
|
||||
$(React.findDOMNode(this.refs.dropdown)).off('hide.bs.dropdown');
|
||||
$(ReactDOM.findDOMNode(this.refs.dropdown)).off('hide.bs.dropdown');
|
||||
}
|
||||
onListenerChange() {
|
||||
var newState = getStateFromStores();
|
||||
|
||||
@@ -25,7 +25,7 @@ export default class NewChannelModal extends React.Component {
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const displayName = React.findDOMNode(this.refs.display_name).value.trim();
|
||||
const displayName = ReactDOM.findDOMNode(this.refs.display_name).value.trim();
|
||||
if (displayName.length < 1) {
|
||||
this.setState({displayNameError: 'This field is required'});
|
||||
return;
|
||||
@@ -35,8 +35,8 @@ export default class NewChannelModal extends React.Component {
|
||||
}
|
||||
handleChange() {
|
||||
const newData = {
|
||||
displayName: React.findDOMNode(this.refs.display_name).value,
|
||||
description: React.findDOMNode(this.refs.channel_desc).value
|
||||
displayName: ReactDOM.findDOMNode(this.refs.display_name).value,
|
||||
description: ReactDOM.findDOMNode(this.refs.channel_desc).value
|
||||
};
|
||||
this.props.onDataChanged(newData);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export default class PasswordResetForm extends React.Component {
|
||||
e.preventDefault();
|
||||
var state = {};
|
||||
|
||||
var password = React.findDOMNode(this.refs.password).value.trim();
|
||||
var password = ReactDOM.findDOMNode(this.refs.password).value.trim();
|
||||
if (!password || password.length < 5) {
|
||||
state.error = 'Please enter at least 5 characters.';
|
||||
this.setState(state);
|
||||
|
||||
@@ -16,7 +16,7 @@ export default class PasswordResetSendLink extends React.Component {
|
||||
e.preventDefault();
|
||||
var state = {};
|
||||
|
||||
var email = React.findDOMNode(this.refs.email).value.trim().toLowerCase();
|
||||
var email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
|
||||
if (!email || !Utils.isEmail(email)) {
|
||||
state.error = 'Please enter a valid email address.';
|
||||
this.setState(state);
|
||||
@@ -33,7 +33,7 @@ export default class PasswordResetSendLink extends React.Component {
|
||||
client.sendPasswordReset(data,
|
||||
function passwordResetSent() {
|
||||
this.setState({error: null, updateText: <p>A password reset link has been sent to <b>{email}</b> for your <b>{this.props.teamDisplayName}</b> team on {window.location.hostname}.</p>, moreUpdateText: 'Please check your inbox.'});
|
||||
$(React.findDOMNode(this.refs.reset_form)).hide();
|
||||
$(ReactDOM.findDOMNode(this.refs.reset_form)).hide();
|
||||
}.bind(this),
|
||||
function passwordResetFailedToSend(err) {
|
||||
this.setState({error: err.message, update_text: null, moreUpdateText: null});
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// See License.txt for license information.
|
||||
|
||||
var UserStore = require('../stores/user_store.jsx');
|
||||
var Popover = ReactBootstrap.Popover;
|
||||
var OverlayTrigger = ReactBootstrap.OverlayTrigger;
|
||||
|
||||
export default class PopoverListMembers extends React.Component {
|
||||
componentDidMount() {
|
||||
@@ -24,16 +26,9 @@ export default class PopoverListMembers extends React.Component {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$('#member_popover').popover({placement: 'bottom', trigger: 'click', html: true});
|
||||
$('body').on('click', function onClick(e) {
|
||||
if (e.target.parentNode && $(e.target.parentNode.parentNode)[0] !== $('#member_popover')[0] && $(e.target).parents('.popover.in').length === 0) {
|
||||
$('#member_popover').popover('hide');
|
||||
}
|
||||
});
|
||||
}
|
||||
render() {
|
||||
let popoverHtml = '';
|
||||
let popoverHtml = [];
|
||||
let count = 0;
|
||||
let countText = '-';
|
||||
const members = this.props.members;
|
||||
@@ -46,7 +41,7 @@ export default class PopoverListMembers extends React.Component {
|
||||
|
||||
members.forEach(function addMemberElement(m) {
|
||||
if (teamMembers[m.username] && teamMembers[m.username].delete_at <= 0) {
|
||||
popoverHtml += `<div class='text--nowrap'>${m.username}</div>`;
|
||||
popoverHtml.push(<div className='text--nowrap'>{m.username}</div>);
|
||||
count++;
|
||||
}
|
||||
});
|
||||
@@ -59,15 +54,14 @@ export default class PopoverListMembers extends React.Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
id='member_popover'
|
||||
data-toggle='popover'
|
||||
data-content={popoverHtml}
|
||||
data-original-title='Members'
|
||||
<OverlayTrigger
|
||||
trigger='click'
|
||||
placement='bottom'
|
||||
rootClose='true'
|
||||
overlay={<Popover title='Members'>{popoverHtml}</Popover>}
|
||||
>
|
||||
<div
|
||||
id='member_tooltip'
|
||||
>
|
||||
<div id='member_popover'>
|
||||
<div>
|
||||
{countText}
|
||||
<span
|
||||
className='fa fa-user'
|
||||
@@ -75,6 +69,7 @@ export default class PopoverListMembers extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ export default class PostBody extends React.Component {
|
||||
}
|
||||
|
||||
parseEmojis() {
|
||||
twemoji.parse(React.findDOMNode(this), {size: Constants.EMOJI_SIZE});
|
||||
twemoji.parse(ReactDOM.findDOMNode(this), {size: Constants.EMOJI_SIZE});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
||||
@@ -15,7 +15,7 @@ export default class PostDeletedModal extends React.Component {
|
||||
this.state = {};
|
||||
}
|
||||
componentDidMount() {
|
||||
$(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', () => {
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', () => {
|
||||
this.handleClose();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ export default class PostList extends React.Component {
|
||||
PreferenceStore.addChangeListener(this.onTimeChange);
|
||||
SocketStore.addChangeListener(this.onSocketChange);
|
||||
|
||||
const postHolder = $(React.findDOMNode(this.refs.postlist));
|
||||
const postHolder = $(ReactDOM.findDOMNode(this.refs.postlist));
|
||||
|
||||
$(window).resize(() => {
|
||||
this.resize();
|
||||
@@ -135,7 +135,7 @@ export default class PostList extends React.Component {
|
||||
|
||||
$('.top-visible-post').removeClass('top-visible-post');
|
||||
|
||||
$(React.findDOMNode(this.refs.postlistcontent)).children().each(function select() {
|
||||
$(ReactDOM.findDOMNode(this.refs.postlistcontent)).children().each(function select() {
|
||||
if ($(this).position().top + $(this).height() / 2 > 0) {
|
||||
$(this).addClass('top-visible-post');
|
||||
return false;
|
||||
@@ -161,7 +161,7 @@ export default class PostList extends React.Component {
|
||||
PreferenceStore.removeChangeListener(this.onTimeChange);
|
||||
$('body').off('click.userpopover');
|
||||
$(window).off('resize');
|
||||
var postHolder = $(React.findDOMNode(this.refs.postlist));
|
||||
var postHolder = $(ReactDOM.findDOMNode(this.refs.postlist));
|
||||
postHolder.off('scroll');
|
||||
}
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
@@ -217,7 +217,7 @@ export default class PostList extends React.Component {
|
||||
}
|
||||
}
|
||||
componentWillUpdate() {
|
||||
var postHolder = $(React.findDOMNode(this.refs.postlist));
|
||||
var postHolder = $(ReactDOM.findDOMNode(this.refs.postlist));
|
||||
this.prevScrollTop = postHolder.scrollTop();
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
@@ -228,7 +228,7 @@ export default class PostList extends React.Component {
|
||||
}
|
||||
}
|
||||
resize() {
|
||||
const postHolder = $(React.findDOMNode(this.refs.postlist));
|
||||
const postHolder = $(ReactDOM.findDOMNode(this.refs.postlist));
|
||||
if ($('#create_post').length > 0) {
|
||||
const height = $(window).height() - $('#create_post').height() - $('#error_bar').outerHeight() - 50;
|
||||
postHolder.css('height', height + 'px');
|
||||
@@ -236,12 +236,12 @@ export default class PostList extends React.Component {
|
||||
}
|
||||
scrollTo(val) {
|
||||
this.isUserScroll = false;
|
||||
var postHolder = $(React.findDOMNode(this.refs.postlist));
|
||||
var postHolder = $(ReactDOM.findDOMNode(this.refs.postlist));
|
||||
postHolder[0].scrollTop = val;
|
||||
}
|
||||
scrollToBottom(force) {
|
||||
this.isUserScroll = false;
|
||||
var postHolder = $(React.findDOMNode(this.refs.postlist));
|
||||
var postHolder = $(ReactDOM.findDOMNode(this.refs.postlist));
|
||||
if ($('#new_message_' + this.props.channelId)[0] && !this.userHasSeenNew && !force) {
|
||||
$('#new_message_' + this.props.channelId)[0].scrollIntoView();
|
||||
} else {
|
||||
@@ -599,14 +599,14 @@ export default class PostList extends React.Component {
|
||||
var order = this.state.postList.order;
|
||||
var channelId = this.props.channelId;
|
||||
|
||||
$(React.findDOMNode(this.refs.loadmore)).text('Retrieving more messages...');
|
||||
$(ReactDOM.findDOMNode(this.refs.loadmore)).text('Retrieving more messages...');
|
||||
|
||||
Client.getPostsPage(
|
||||
channelId,
|
||||
order.length,
|
||||
Constants.POST_CHUNK_SIZE,
|
||||
function success(data) {
|
||||
$(React.findDOMNode(this.refs.loadmore)).text('Load more messages');
|
||||
$(ReactDOM.findDOMNode(this.refs.loadmore)).text('Load more messages');
|
||||
this.gotMorePosts = true;
|
||||
this.setState({numToDisplay: this.state.numToDisplay + Constants.POST_CHUNK_SIZE});
|
||||
|
||||
@@ -631,7 +631,7 @@ export default class PostList extends React.Component {
|
||||
Client.getProfiles();
|
||||
}.bind(this),
|
||||
function fail(err) {
|
||||
$(React.findDOMNode(this.refs.loadmore)).text('Load more messages');
|
||||
$(ReactDOM.findDOMNode(this.refs.loadmore)).text('Load more messages');
|
||||
AsyncClient.dispatchError(err, 'getPosts');
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
@@ -14,7 +14,7 @@ export default class RegisterAppModal extends React.Component {
|
||||
this.state = {clientId: '', clientSecret: '', saved: false};
|
||||
}
|
||||
componentDidMount() {
|
||||
$(React.findDOMNode(this)).on('hide.bs.modal', this.onHide);
|
||||
$(ReactDOM.findDOMNode(this)).on('hide.bs.modal', this.onHide);
|
||||
}
|
||||
register() {
|
||||
var state = this.state;
|
||||
@@ -22,7 +22,7 @@ export default class RegisterAppModal extends React.Component {
|
||||
|
||||
var app = {};
|
||||
|
||||
var name = this.refs.name.getDOMNode().value;
|
||||
var name = this.refs.name.value;
|
||||
if (!name || name.length === 0) {
|
||||
state.nameError = 'Application name must be filled in.';
|
||||
this.setState(state);
|
||||
@@ -31,7 +31,7 @@ export default class RegisterAppModal extends React.Component {
|
||||
state.nameError = null;
|
||||
app.name = name;
|
||||
|
||||
var homepage = this.refs.homepage.getDOMNode().value;
|
||||
var homepage = this.refs.homepage.value;
|
||||
if (!homepage || homepage.length === 0) {
|
||||
state.homepageError = 'Homepage must be filled in.';
|
||||
this.setState(state);
|
||||
@@ -40,10 +40,10 @@ export default class RegisterAppModal extends React.Component {
|
||||
state.homepageError = null;
|
||||
app.homepage = homepage;
|
||||
|
||||
var desc = this.refs.desc.getDOMNode().value;
|
||||
var desc = this.refs.desc.value;
|
||||
app.description = desc;
|
||||
|
||||
var rawCallbacks = this.refs.callback.getDOMNode().value.trim();
|
||||
var rawCallbacks = this.refs.callback.value.trim();
|
||||
if (!rawCallbacks || rawCallbacks.length === 0) {
|
||||
state.callbackError = 'At least one callback URL must be filled in.';
|
||||
this.setState(state);
|
||||
@@ -73,7 +73,7 @@ export default class RegisterAppModal extends React.Component {
|
||||
this.setState({clientId: '', clientSecret: '', saved: false});
|
||||
}
|
||||
save() {
|
||||
this.setState({saved: this.refs.save.getDOMNode().checked});
|
||||
this.setState({saved: this.refs.save.checked});
|
||||
}
|
||||
render() {
|
||||
var nameError;
|
||||
|
||||
@@ -37,13 +37,13 @@ export default class RemovedFromChannelModal extends React.Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
$(React.findDOMNode(this)).on('show.bs.modal', this.handleShow);
|
||||
$(React.findDOMNode(this)).on('hidden.bs.modal', this.handleClose);
|
||||
$(ReactDOM.findDOMNode(this)).on('show.bs.modal', this.handleShow);
|
||||
$(ReactDOM.findDOMNode(this)).on('hidden.bs.modal', this.handleClose);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
$(React.findDOMNode(this)).off('show.bs.modal', this.handleShow);
|
||||
$(React.findDOMNode(this)).off('hidden.bs.modal', this.handleClose);
|
||||
$(ReactDOM.findDOMNode(this)).off('show.bs.modal', this.handleShow);
|
||||
$(ReactDOM.findDOMNode(this)).off('hidden.bs.modal', this.handleClose);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -76,13 +76,13 @@ export default class RenameChannelModal extends React.Component {
|
||||
|
||||
Client.updateChannel(channel,
|
||||
function handleUpdateSuccess() {
|
||||
$(React.findDOMNode(this.refs.modal)).modal('hide');
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).modal('hide');
|
||||
|
||||
AsyncClient.getChannel(channel.id);
|
||||
Utils.updateAddressBar(channel.name);
|
||||
|
||||
React.findDOMNode(this.refs.displayName).value = '';
|
||||
React.findDOMNode(this.refs.channelName).value = '';
|
||||
ReactDOM.findDOMNode(this.refs.displayName).value = '';
|
||||
ReactDOM.findDOMNode(this.refs.channelName).value = '';
|
||||
}.bind(this),
|
||||
function handleUpdateError(err) {
|
||||
state.serverError = err.message;
|
||||
@@ -92,15 +92,15 @@ export default class RenameChannelModal extends React.Component {
|
||||
);
|
||||
}
|
||||
onNameChange() {
|
||||
this.setState({channelName: React.findDOMNode(this.refs.channelName).value});
|
||||
this.setState({channelName: ReactDOM.findDOMNode(this.refs.channelName).value});
|
||||
}
|
||||
onDisplayNameChange() {
|
||||
this.setState({displayName: React.findDOMNode(this.refs.displayName).value});
|
||||
this.setState({displayName: ReactDOM.findDOMNode(this.refs.displayName).value});
|
||||
}
|
||||
displayNameKeyUp() {
|
||||
const displayName = React.findDOMNode(this.refs.displayName).value.trim();
|
||||
const displayName = ReactDOM.findDOMNode(this.refs.displayName).value.trim();
|
||||
const channelName = Utils.cleanUpUrlable(displayName);
|
||||
React.findDOMNode(this.refs.channelName).value = channelName;
|
||||
ReactDOM.findDOMNode(this.refs.channelName).value = channelName;
|
||||
this.setState({channelName: channelName});
|
||||
}
|
||||
handleClose() {
|
||||
@@ -119,11 +119,11 @@ export default class RenameChannelModal extends React.Component {
|
||||
this.setState({displayName: button.attr('data-display'), channelName: button.attr('data-name'), channelId: button.attr('data-channelid')});
|
||||
}
|
||||
componentDidMount() {
|
||||
$(React.findDOMNode(this.refs.modal)).on('show.bs.modal', this.handleShow);
|
||||
$(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.handleClose);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', this.handleShow);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', this.handleClose);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
$(React.findDOMNode(this.refs.modal)).off('hidden.bs.modal', this.handleClose);
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).off('hidden.bs.modal', this.handleClose);
|
||||
}
|
||||
render() {
|
||||
let displayNameError = null;
|
||||
|
||||
@@ -55,7 +55,7 @@ export default class RhsComment extends React.Component {
|
||||
this.forceUpdate();
|
||||
}
|
||||
parseEmojis() {
|
||||
twemoji.parse(React.findDOMNode(this), {size: Constants.EMOJI_SIZE});
|
||||
twemoji.parse(ReactDOM.findDOMNode(this), {size: Constants.EMOJI_SIZE});
|
||||
}
|
||||
componentDidMount() {
|
||||
this.parseEmojis();
|
||||
|
||||
@@ -19,7 +19,7 @@ export default class RhsRootPost extends React.Component {
|
||||
this.state = {};
|
||||
}
|
||||
parseEmojis() {
|
||||
twemoji.parse(React.findDOMNode(this), {size: Constants.EMOJI_SIZE});
|
||||
twemoji.parse(ReactDOM.findDOMNode(this), {size: Constants.EMOJI_SIZE});
|
||||
}
|
||||
componentDidMount() {
|
||||
this.parseEmojis();
|
||||
|
||||
@@ -55,8 +55,6 @@ export default class RhsThread extends React.Component {
|
||||
if ($('.post-right__scroll')[0]) {
|
||||
$('.post-right__scroll').scrollTop($('.post-right__scroll')[0].scrollHeight);
|
||||
}
|
||||
|
||||
$('.post-right__scroll').perfectScrollbar('update');
|
||||
this.resize();
|
||||
}
|
||||
componentWillUnmount() {
|
||||
@@ -113,8 +111,10 @@ export default class RhsThread extends React.Component {
|
||||
var height = $(window).height() - $('#error_bar').outerHeight() - 100;
|
||||
$('.post-right__scroll').css('height', height + 'px');
|
||||
$('.post-right__scroll').scrollTop(100000);
|
||||
$('.post-right__scroll').perfectScrollbar();
|
||||
$('.post-right__scroll').perfectScrollbar('update');
|
||||
if ($(window).width() > 768) {
|
||||
$('.post-right__scroll').perfectScrollbar();
|
||||
$('.post-right__scroll').perfectScrollbar('update');
|
||||
}
|
||||
}
|
||||
render() {
|
||||
var postList = this.state.postList;
|
||||
|
||||
@@ -90,7 +90,7 @@ export default class SearchBar extends React.Component {
|
||||
function success(data) {
|
||||
this.setState({isSearching: false});
|
||||
if (utils.isMobile()) {
|
||||
React.findDOMNode(this.refs.search).value = '';
|
||||
ReactDOM.findDOMNode(this.refs.search).value = '';
|
||||
}
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
|
||||
@@ -56,7 +56,9 @@ export default class SearchResults extends React.Component {
|
||||
var height = $(window).height() - $('#error_bar').outerHeight() - 100;
|
||||
$('#search-items-container').css('height', height + 'px');
|
||||
$('#search-items-container').scrollTop(0);
|
||||
$('#search-items-container').perfectScrollbar();
|
||||
if ($(window).width() > 768) {
|
||||
$('#search-items-container').perfectScrollbar();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -12,7 +12,7 @@ export default class SettingPicture extends React.Component {
|
||||
if (file) {
|
||||
var reader = new FileReader();
|
||||
|
||||
var img = React.findDOMNode(this.refs.image);
|
||||
var img = ReactDOM.findDOMNode(this.refs.image);
|
||||
reader.onload = function load(e) {
|
||||
$(img).attr('src', e.target.result);
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@ export default class SettingsUpload extends React.Component {
|
||||
|
||||
doSubmit(e) {
|
||||
e.preventDefault();
|
||||
var inputnode = React.findDOMNode(this.refs.uploadinput);
|
||||
var inputnode = ReactDOM.findDOMNode(this.refs.uploadinput);
|
||||
if (inputnode.files && inputnode.files[0]) {
|
||||
this.props.submit(inputnode.files[0]);
|
||||
} else {
|
||||
|
||||
@@ -8,6 +8,7 @@ const Client = require('../utils/client.jsx');
|
||||
const Constants = require('../utils/constants.jsx');
|
||||
const PreferenceStore = require('../stores/preference_store.jsx');
|
||||
const NewChannelFlow = require('./new_channel_flow.jsx');
|
||||
const MoreDirectChannels = require('./more_direct_channels.jsx');
|
||||
const SearchBox = require('./search_bar.jsx');
|
||||
const SidebarHeader = require('./sidebar_header.jsx');
|
||||
const SocketStore = require('../stores/socket_store.jsx');
|
||||
@@ -33,12 +34,19 @@ export default class Sidebar extends React.Component {
|
||||
this.onResize = this.onResize.bind(this);
|
||||
this.updateUnreadIndicators = this.updateUnreadIndicators.bind(this);
|
||||
this.handleLeaveDirectChannel = this.handleLeaveDirectChannel.bind(this);
|
||||
|
||||
this.showNewChannelModal = this.showNewChannelModal.bind(this);
|
||||
this.hideNewChannelModal = this.hideNewChannelModal.bind(this);
|
||||
this.showMoreDirectChannelsModal = this.showMoreDirectChannelsModal.bind(this);
|
||||
this.hideMoreDirectChannelsModal = this.hideMoreDirectChannelsModal.bind(this);
|
||||
|
||||
this.createChannelElement = this.createChannelElement.bind(this);
|
||||
|
||||
this.isLeaving = new Map();
|
||||
|
||||
const state = this.getStateFromStores();
|
||||
state.modal = '';
|
||||
state.newChannelModalType = '';
|
||||
state.showMoreDirectChannelsModal = false;
|
||||
state.loadingDMChannel = -1;
|
||||
|
||||
this.state = state;
|
||||
@@ -47,10 +55,11 @@ export default class Sidebar extends React.Component {
|
||||
const members = ChannelStore.getAllMembers();
|
||||
var teamMemberMap = UserStore.getActiveOnlyProfiles();
|
||||
var currentId = ChannelStore.getCurrentId();
|
||||
const currentUserId = UserStore.getCurrentId();
|
||||
|
||||
var teammates = [];
|
||||
for (var id in teamMemberMap) {
|
||||
if (id === UserStore.getCurrentId()) {
|
||||
if (id === currentUserId) {
|
||||
continue;
|
||||
}
|
||||
teammates.push(teamMemberMap[id]);
|
||||
@@ -58,22 +67,16 @@ export default class Sidebar extends React.Component {
|
||||
|
||||
const preferences = PreferenceStore.getPreferences(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW);
|
||||
|
||||
// Create lists of all read and unread direct channels
|
||||
var visibleDirectChannels = [];
|
||||
var hiddenDirectChannels = [];
|
||||
var hiddenDirectChannelCount = 0;
|
||||
for (var i = 0; i < teammates.length; i++) {
|
||||
const teammate = teammates[i];
|
||||
|
||||
if (teammate.id === UserStore.getCurrentId()) {
|
||||
if (teammate.id === currentUserId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var channelName = '';
|
||||
if (teammate.id > UserStore.getCurrentId()) {
|
||||
channelName = UserStore.getCurrentId() + '__' + teammate.id;
|
||||
} else {
|
||||
channelName = teammate.id + '__' + UserStore.getCurrentId();
|
||||
}
|
||||
const channelName = Utils.getDirectChannelName(currentUserId, teammate.id);
|
||||
|
||||
let forceShow = false;
|
||||
let channel = ChannelStore.getByName(channelName);
|
||||
@@ -106,19 +109,18 @@ export default class Sidebar extends React.Component {
|
||||
|
||||
visibleDirectChannels.push(channel);
|
||||
} else {
|
||||
hiddenDirectChannels.push(channel);
|
||||
hiddenDirectChannelCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
visibleDirectChannels.sort(this.sortChannelsByDisplayName);
|
||||
hiddenDirectChannels.sort(this.sortChannelsByDisplayName);
|
||||
|
||||
return {
|
||||
activeId: currentId,
|
||||
channels: ChannelStore.getAll(),
|
||||
members,
|
||||
visibleDirectChannels,
|
||||
hiddenDirectChannels
|
||||
hiddenDirectChannelCount
|
||||
};
|
||||
}
|
||||
|
||||
@@ -132,7 +134,9 @@ export default class Sidebar extends React.Component {
|
||||
|
||||
AsyncClient.getDirectChannelPreferences();
|
||||
|
||||
$('.nav-pills__container').perfectScrollbar();
|
||||
if ($(window).width() > 768) {
|
||||
$('.nav-pills__container').perfectScrollbar();
|
||||
}
|
||||
|
||||
this.updateTitle();
|
||||
this.updateUnreadIndicators();
|
||||
@@ -280,13 +284,13 @@ export default class Sidebar extends React.Component {
|
||||
this.updateUnreadIndicators();
|
||||
}
|
||||
updateUnreadIndicators() {
|
||||
const container = $(React.findDOMNode(this.refs.container));
|
||||
const container = $(ReactDOM.findDOMNode(this.refs.container));
|
||||
|
||||
var showTopUnread = false;
|
||||
var showBottomUnread = false;
|
||||
|
||||
if (this.firstUnreadChannel) {
|
||||
var firstUnreadElement = $(React.findDOMNode(this.refs[this.firstUnreadChannel]));
|
||||
var firstUnreadElement = $(ReactDOM.findDOMNode(this.refs[this.firstUnreadChannel]));
|
||||
|
||||
if (firstUnreadElement.position().top + firstUnreadElement.height() < 0) {
|
||||
showTopUnread = true;
|
||||
@@ -294,7 +298,7 @@ export default class Sidebar extends React.Component {
|
||||
}
|
||||
|
||||
if (this.lastUnreadChannel) {
|
||||
var lastUnreadElement = $(React.findDOMNode(this.refs[this.lastUnreadChannel]));
|
||||
var lastUnreadElement = $(ReactDOM.findDOMNode(this.refs[this.lastUnreadChannel]));
|
||||
|
||||
if (lastUnreadElement.position().top > container.height()) {
|
||||
showBottomUnread = true;
|
||||
@@ -336,6 +340,20 @@ export default class Sidebar extends React.Component {
|
||||
return a.display_name.localeCompare(b.display_name);
|
||||
}
|
||||
|
||||
showNewChannelModal(type) {
|
||||
this.setState({newChannelModalType: type});
|
||||
}
|
||||
hideNewChannelModal() {
|
||||
this.setState({newChannelModalType: ''});
|
||||
}
|
||||
|
||||
showMoreDirectChannelsModal() {
|
||||
this.setState({showDirectChannelsModal: true});
|
||||
}
|
||||
hideMoreDirectChannelsModal() {
|
||||
this.setState({showDirectChannelsModal: false});
|
||||
}
|
||||
|
||||
createChannelElement(channel, index, arr, handleClose) {
|
||||
var members = this.state.members;
|
||||
var activeId = this.state.activeId;
|
||||
@@ -532,25 +550,21 @@ export default class Sidebar extends React.Component {
|
||||
head.appendChild(link);
|
||||
|
||||
var directMessageMore = null;
|
||||
if (this.state.hiddenDirectChannels.length > 0) {
|
||||
if (this.state.hiddenDirectChannelCount > 0) {
|
||||
directMessageMore = (
|
||||
<li key='more'>
|
||||
<a
|
||||
key={`more${this.state.hiddenDirectChannels.length}`}
|
||||
href='#'
|
||||
data-toggle='modal'
|
||||
className='nav-more'
|
||||
data-target='#more_direct_channels'
|
||||
data-channels={JSON.stringify(this.state.hiddenDirectChannels)}
|
||||
onClick={this.showMoreDirectChannelsModal}
|
||||
>
|
||||
{'More (' + this.state.hiddenDirectChannels.length + ')'}
|
||||
{'More (' + this.state.hiddenDirectChannelCount + ')'}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
let showChannelModal = false;
|
||||
if (this.state.modal !== '') {
|
||||
if (this.state.newChannelModalType !== '') {
|
||||
showChannelModal = true;
|
||||
}
|
||||
|
||||
@@ -561,9 +575,14 @@ export default class Sidebar extends React.Component {
|
||||
<div>
|
||||
<NewChannelFlow
|
||||
show={showChannelModal}
|
||||
channelType={this.state.modal}
|
||||
onModalDismissed={() => this.setState({modal: ''})}
|
||||
channelType={this.state.newChannelModalType}
|
||||
onModalDismissed={this.hideNewChannelModal}
|
||||
/>
|
||||
<MoreDirectChannels
|
||||
show={this.state.showDirectChannelsModal}
|
||||
onModalDismissed={this.hideMoreDirectChannelsModal}
|
||||
/>
|
||||
|
||||
<SidebarHeader
|
||||
teamDisplayName={this.props.teamDisplayName}
|
||||
teamName={this.props.teamName}
|
||||
@@ -599,7 +618,7 @@ export default class Sidebar extends React.Component {
|
||||
<a
|
||||
className='add-channel-btn'
|
||||
href='#'
|
||||
onClick={() => this.setState({modal: 'O'})}
|
||||
onClick={this.showNewChannelModal.bind(this, 'O')}
|
||||
>
|
||||
{'+'}
|
||||
</a>
|
||||
@@ -632,7 +651,7 @@ export default class Sidebar extends React.Component {
|
||||
<a
|
||||
className='add-channel-btn'
|
||||
href='#'
|
||||
onClick={() => this.setState({modal: 'P'})}
|
||||
onClick={this.showNewChannelModal.bind(this, 'P')}
|
||||
>
|
||||
{'+'}
|
||||
</a>
|
||||
|
||||
@@ -28,7 +28,7 @@ export default class SignupUserComplete extends React.Component {
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const providedEmail = React.findDOMNode(this.refs.email).value.trim();
|
||||
const providedEmail = ReactDOM.findDOMNode(this.refs.email).value.trim();
|
||||
if (!providedEmail) {
|
||||
this.setState({nameError: '', emailError: 'This field is required', passwordError: ''});
|
||||
return;
|
||||
@@ -39,7 +39,7 @@ export default class SignupUserComplete extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const providedUsername = React.findDOMNode(this.refs.name).value.trim().toLowerCase();
|
||||
const providedUsername = ReactDOM.findDOMNode(this.refs.name).value.trim().toLowerCase();
|
||||
if (!providedUsername) {
|
||||
this.setState({nameError: 'This field is required', emailError: '', passwordError: '', serverError: ''});
|
||||
return;
|
||||
@@ -59,7 +59,7 @@ export default class SignupUserComplete extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const providedPassword = React.findDOMNode(this.refs.password).value.trim();
|
||||
const providedPassword = ReactDOM.findDOMNode(this.refs.password).value.trim();
|
||||
if (!providedPassword || providedPassword.length < 5) {
|
||||
this.setState({nameError: '', emailError: '', passwordError: 'Please enter at least 5 characters', serverError: ''});
|
||||
return;
|
||||
|
||||
@@ -44,11 +44,11 @@ export default class TeamMembers extends React.Component {
|
||||
UserStore.addChangeListener(this.onChange);
|
||||
|
||||
var self = this;
|
||||
$(React.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function show() {
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', function show() {
|
||||
self.setState({render_members: false});
|
||||
});
|
||||
|
||||
$(React.findDOMNode(this.refs.modal)).on('show.bs.modal', function hide() {
|
||||
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', function hide() {
|
||||
self.setState({render_members: true});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export default class TeamSignupDisplayNamePage extends React.Component {
|
||||
submitNext(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var displayName = React.findDOMNode(this.refs.name).value.trim();
|
||||
var displayName = ReactDOM.findDOMNode(this.refs.name).value.trim();
|
||||
if (!displayName) {
|
||||
this.setState({nameError: 'This field is required'});
|
||||
return;
|
||||
|
||||
@@ -13,10 +13,10 @@ export default class TeamSignupEmailItem extends React.Component {
|
||||
this.state = {};
|
||||
}
|
||||
getValue() {
|
||||
return React.findDOMNode(this.refs.email).value.trim();
|
||||
return ReactDOM.findDOMNode(this.refs.email).value.trim();
|
||||
}
|
||||
validate(teamEmail) {
|
||||
const email = React.findDOMNode(this.refs.email).value.trim().toLowerCase();
|
||||
const email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
|
||||
|
||||
if (!email) {
|
||||
return true;
|
||||
|
||||
@@ -22,7 +22,7 @@ export default class TeamSignupPasswordPage extends React.Component {
|
||||
submitNext(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var password = React.findDOMNode(this.refs.password).value.trim();
|
||||
var password = ReactDOM.findDOMNode(this.refs.password).value.trim();
|
||||
if (!password || password.length < 5) {
|
||||
this.setState({passwordError: 'Please enter at least 5 characters'});
|
||||
return;
|
||||
|
||||
@@ -23,7 +23,7 @@ export default class TeamSignupUrlPage extends React.Component {
|
||||
submitNext(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const name = React.findDOMNode(this.refs.name).value.trim();
|
||||
const name = ReactDOM.findDOMNode(this.refs.name).value.trim();
|
||||
if (!name) {
|
||||
this.setState({nameError: 'This field is required'});
|
||||
return;
|
||||
|
||||
@@ -21,7 +21,7 @@ export default class TeamSignupUsernamePage extends React.Component {
|
||||
submitNext(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var name = React.findDOMNode(this.refs.name).value.trim().toLowerCase();
|
||||
var name = ReactDOM.findDOMNode(this.refs.name).value.trim().toLowerCase();
|
||||
|
||||
var usernameError = Utils.isValidUsername(name);
|
||||
if (usernameError === 'Cannot use a reserved word as a username.') {
|
||||
|
||||
@@ -36,7 +36,7 @@ export default class TeamSignupWelcomePage extends React.Component {
|
||||
|
||||
var state = {useDiff: true, serverError: ''};
|
||||
|
||||
var email = React.findDOMNode(this.refs.email).value.trim().toLowerCase();
|
||||
var email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
|
||||
if (!email || !Utils.isEmail(email)) {
|
||||
state.emailError = 'Please enter a valid email address';
|
||||
this.setState(state);
|
||||
|
||||
@@ -17,7 +17,7 @@ export default class EmailSignUpPage extends React.Component {
|
||||
var team = {};
|
||||
var state = {serverError: ''};
|
||||
|
||||
team.email = React.findDOMNode(this.refs.email).value.trim().toLowerCase();
|
||||
team.email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
|
||||
if (!team.email || !Utils.isEmail(team.email)) {
|
||||
state.emailError = 'Please enter a valid email address';
|
||||
state.inValid = true;
|
||||
|
||||
@@ -54,7 +54,7 @@ export default class SSOSignUpPage extends React.Component {
|
||||
);
|
||||
}
|
||||
nameChange() {
|
||||
this.setState({name: React.findDOMNode(this.refs.teamname).value.trim()});
|
||||
this.setState({name: ReactDOM.findDOMNode(this.refs.teamname).value.trim()});
|
||||
}
|
||||
render() {
|
||||
var nameError = null;
|
||||
|
||||
@@ -9,6 +9,7 @@ const ErrorStore = require('../stores/error_store.jsx');
|
||||
const Utils = require('../utils/utils.jsx');
|
||||
const Constants = require('../utils/constants.jsx');
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
const KeyCodes = Constants.KeyCodes;
|
||||
|
||||
export default class Textbox extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -83,7 +84,7 @@ export default class Textbox extends React.Component {
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.caret >= 0) {
|
||||
Utils.setCaretPosition(React.findDOMNode(this.refs.message), this.caret);
|
||||
Utils.setCaretPosition(ReactDOM.findDOMNode(this.refs.message), this.caret);
|
||||
this.caret = -1;
|
||||
}
|
||||
if (this.doProcessMentions) {
|
||||
@@ -97,7 +98,7 @@ export default class Textbox extends React.Component {
|
||||
if (!this.addedMention) {
|
||||
this.checkForNewMention(nextProps.messageText);
|
||||
}
|
||||
const text = React.findDOMNode(this.refs.message).value;
|
||||
const text = ReactDOM.findDOMNode(this.refs.message).value;
|
||||
if (nextProps.channelId !== this.props.channelId || nextProps.messageText !== text) {
|
||||
this.doProcessMentions = true;
|
||||
}
|
||||
@@ -117,11 +118,11 @@ export default class Textbox extends React.Component {
|
||||
}
|
||||
|
||||
handleChange() {
|
||||
this.props.onUserInput(React.findDOMNode(this.refs.message).value);
|
||||
this.props.onUserInput(ReactDOM.findDOMNode(this.refs.message).value);
|
||||
}
|
||||
|
||||
handleKeyPress(e) {
|
||||
const text = React.findDOMNode(this.refs.message).value;
|
||||
const text = ReactDOM.findDOMNode(this.refs.message).value;
|
||||
|
||||
if (!this.refs.commands.isEmpty() && text.indexOf('/') === 0 && e.which === 13) {
|
||||
this.refs.commands.addFirstCommand();
|
||||
@@ -130,7 +131,7 @@ export default class Textbox extends React.Component {
|
||||
}
|
||||
|
||||
if (!this.doProcessMentions) {
|
||||
const caret = Utils.getCaretPosition(React.findDOMNode(this.refs.message));
|
||||
const caret = Utils.getCaretPosition(ReactDOM.findDOMNode(this.refs.message));
|
||||
const preText = text.substring(0, caret);
|
||||
const lastSpace = preText.lastIndexOf(' ');
|
||||
const lastAt = preText.lastIndexOf('@');
|
||||
@@ -144,17 +145,19 @@ export default class Textbox extends React.Component {
|
||||
}
|
||||
|
||||
handleKeyDown(e) {
|
||||
if (Utils.getSelectedText(React.findDOMNode(this.refs.message)) !== '') {
|
||||
if (Utils.getSelectedText(ReactDOM.findDOMNode(this.refs.message)) !== '') {
|
||||
this.doProcessMentions = true;
|
||||
}
|
||||
|
||||
if (e.keyCode === 8) {
|
||||
if (e.keyCode === KeyCodes.BACKSPACE) {
|
||||
this.handleBackspace(e);
|
||||
} else if (this.props.onKeyDown) {
|
||||
this.props.onKeyDown(e);
|
||||
}
|
||||
}
|
||||
|
||||
handleBackspace() {
|
||||
const text = React.findDOMNode(this.refs.message).value;
|
||||
const text = ReactDOM.findDOMNode(this.refs.message).value;
|
||||
if (text.indexOf('/') === 0) {
|
||||
this.refs.commands.getSuggestedCommands(text.substring(0, text.length - 1));
|
||||
}
|
||||
@@ -163,7 +166,7 @@ export default class Textbox extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const caret = Utils.getCaretPosition(React.findDOMNode(this.refs.message));
|
||||
const caret = Utils.getCaretPosition(ReactDOM.findDOMNode(this.refs.message));
|
||||
const preText = text.substring(0, caret);
|
||||
const lastSpace = preText.lastIndexOf(' ');
|
||||
const lastAt = preText.lastIndexOf('@');
|
||||
@@ -174,7 +177,7 @@ export default class Textbox extends React.Component {
|
||||
}
|
||||
|
||||
checkForNewMention(text) {
|
||||
const caret = Utils.getCaretPosition(React.findDOMNode(this.refs.message));
|
||||
const caret = Utils.getCaretPosition(ReactDOM.findDOMNode(this.refs.message));
|
||||
|
||||
const preText = text.substring(0, caret);
|
||||
|
||||
@@ -201,7 +204,7 @@ export default class Textbox extends React.Component {
|
||||
}
|
||||
|
||||
addMention(name) {
|
||||
const caret = Utils.getCaretPosition(React.findDOMNode(this.refs.message));
|
||||
const caret = Utils.getCaretPosition(ReactDOM.findDOMNode(this.refs.message));
|
||||
|
||||
const text = this.props.messageText;
|
||||
|
||||
@@ -224,14 +227,14 @@ export default class Textbox extends React.Component {
|
||||
}
|
||||
|
||||
addCommand(cmd) {
|
||||
const elm = React.findDOMNode(this.refs.message);
|
||||
const elm = ReactDOM.findDOMNode(this.refs.message);
|
||||
elm.value = cmd;
|
||||
this.handleChange();
|
||||
}
|
||||
|
||||
resize() {
|
||||
const e = React.findDOMNode(this.refs.message);
|
||||
const w = React.findDOMNode(this.refs.wrapper);
|
||||
const e = ReactDOM.findDOMNode(this.refs.message);
|
||||
const w = ReactDOM.findDOMNode(this.refs.wrapper);
|
||||
|
||||
const prevHeight = $(e).height();
|
||||
|
||||
@@ -259,14 +262,14 @@ export default class Textbox extends React.Component {
|
||||
}
|
||||
|
||||
handleFocus() {
|
||||
const elm = React.findDOMNode(this.refs.message);
|
||||
const elm = ReactDOM.findDOMNode(this.refs.message);
|
||||
if (elm.title === elm.value) {
|
||||
elm.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
handleBlur() {
|
||||
const elm = React.findDOMNode(this.refs.message);
|
||||
const elm = ReactDOM.findDOMNode(this.refs.message);
|
||||
if (elm.value === '') {
|
||||
elm.value = elm.title;
|
||||
}
|
||||
@@ -318,5 +321,6 @@ Textbox.propTypes = {
|
||||
onUserInput: React.PropTypes.func.isRequired,
|
||||
onKeyPress: React.PropTypes.func.isRequired,
|
||||
onHeightChange: React.PropTypes.func,
|
||||
createMessage: React.PropTypes.string.isRequired
|
||||
createMessage: React.PropTypes.string.isRequired,
|
||||
onKeyDown: React.PropTypes.func
|
||||
};
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
var Utils = require('../utils/utils.jsx');
|
||||
var UserStore = require('../stores/user_store.jsx');
|
||||
var Popover = ReactBootstrap.Popover;
|
||||
var OverlayTrigger = ReactBootstrap.OverlayTrigger;
|
||||
|
||||
var id = 0;
|
||||
|
||||
@@ -32,7 +34,6 @@ export default class UserProfile extends React.Component {
|
||||
componentDidMount() {
|
||||
UserStore.addChangeListener(this.onChange);
|
||||
if (!this.props.disablePopover) {
|
||||
$('#profile_' + this.uniqueId).popover({placement: 'right', container: 'body', trigger: 'click hover', html: true, delay: {show: 200, hide: 100}});
|
||||
$('body').tooltip({selector: '[data-toggle=tooltip]', trigger: 'hover click'});
|
||||
}
|
||||
}
|
||||
@@ -62,23 +63,46 @@ export default class UserProfile extends React.Component {
|
||||
return <div>{name}</div>;
|
||||
}
|
||||
|
||||
var dataContent = '<img class="user-popover__image" src="/api/v1/users/' + this.state.profile.id + '/image?time=' + this.state.profile.update_at + '" height="128" width="128" />';
|
||||
var dataContent = [];
|
||||
dataContent.push(
|
||||
<img className='user-popover__image'
|
||||
src={'/api/v1/users/' + this.state.profile.id + '/image?time=' + this.state.profile.update_at}
|
||||
height='128'
|
||||
width='128'
|
||||
/>
|
||||
);
|
||||
if (!global.window.config.ShowEmailAddress === 'true') {
|
||||
dataContent += '<div class="text-nowrap">Email not shared</div>';
|
||||
dataContent.push(<div className='text-nowrap'>{'Email not shared'}</div>);
|
||||
} else {
|
||||
dataContent += '<div data-toggle="tooltip" title="' + this.state.profile.email + '"><a href="mailto:' + this.state.profile.email + '" class="text-nowrap text-lowercase user-popover__email">' + this.state.profile.email + '</a></div>';
|
||||
dataContent.push(
|
||||
<div
|
||||
data-toggle='tooltip'
|
||||
title="' + this.state.profile.email + '"
|
||||
>
|
||||
<a
|
||||
href="mailto:' + this.state.profile.email + '"
|
||||
className='text-nowrap text-lowercase user-popover__email'
|
||||
>
|
||||
{this.state.profile.email}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<OverlayTrigger
|
||||
trigger='click'
|
||||
placement='right'
|
||||
rootClose='true'
|
||||
overlay={<Popover title={this.state.profile.username}>{dataContent}</Popover>}
|
||||
>
|
||||
<div
|
||||
className='user-popover'
|
||||
id={'profile_' + this.uniqueId}
|
||||
data-toggle='popover'
|
||||
data-content={dataContent}
|
||||
data-original-title={this.state.profile.username}
|
||||
>
|
||||
{name}
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export default class ImportThemeModal extends React.Component {
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const text = React.findDOMNode(this.refs.input).value;
|
||||
const text = ReactDOM.findDOMNode(this.refs.input).value;
|
||||
|
||||
if (!this.isInputValid(text)) {
|
||||
this.setState({inputError: 'Invalid format, please try copying and pasting in again.'});
|
||||
|
||||
@@ -29,14 +29,14 @@ export default class UserSettingsAppearance extends React.Component {
|
||||
UserStore.addChangeListener(this.onChange);
|
||||
|
||||
if (this.props.activeSection === 'theme') {
|
||||
$(React.findDOMNode(this.refs[this.state.theme])).addClass('active-border');
|
||||
$(ReactDOM.findDOMNode(this.refs[this.state.theme])).addClass('active-border');
|
||||
}
|
||||
$('#user_settings').on('hidden.bs.modal', this.handleClose);
|
||||
}
|
||||
componentDidUpdate() {
|
||||
if (this.props.activeSection === 'theme') {
|
||||
$('.color-btn').removeClass('active-border');
|
||||
$(React.findDOMNode(this.refs[this.state.theme])).addClass('active-border');
|
||||
$(ReactDOM.findDOMNode(this.refs[this.state.theme])).addClass('active-border');
|
||||
}
|
||||
}
|
||||
componentWillUnmount() {
|
||||
|
||||
@@ -211,7 +211,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
this.props.updateSection(section);
|
||||
}
|
||||
handleClose() {
|
||||
$(React.findDOMNode(this)).find('.form-control').each(function clearForms() {
|
||||
$(ReactDOM.findDOMNode(this)).find('.form-control').each(function clearForms() {
|
||||
this.value = '';
|
||||
});
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ export default class NotificationsTab extends React.Component {
|
||||
);
|
||||
}
|
||||
handleClose() {
|
||||
$(React.findDOMNode(this)).find('.form-control').each(function clearField() {
|
||||
$(ReactDOM.findDOMNode(this)).find('.form-control').each(function clearField() {
|
||||
this.value = '';
|
||||
});
|
||||
|
||||
@@ -158,15 +158,15 @@ export default class NotificationsTab extends React.Component {
|
||||
}
|
||||
handleNotifyRadio(notifyLevel) {
|
||||
this.setState({notifyLevel: notifyLevel});
|
||||
React.findDOMNode(this.refs.wrapper).focus();
|
||||
ReactDOM.findDOMNode(this.refs.wrapper).focus();
|
||||
}
|
||||
handleEmailRadio(enableEmail) {
|
||||
this.setState({enableEmail: enableEmail});
|
||||
React.findDOMNode(this.refs.wrapper).focus();
|
||||
ReactDOM.findDOMNode(this.refs.wrapper).focus();
|
||||
}
|
||||
handleSoundRadio(enableSound) {
|
||||
this.setState({enableSound: enableSound});
|
||||
React.findDOMNode(this.refs.wrapper).focus();
|
||||
ReactDOM.findDOMNode(this.refs.wrapper).focus();
|
||||
}
|
||||
updateUsernameKey(val) {
|
||||
this.setState({usernameKey: val});
|
||||
@@ -184,10 +184,10 @@ export default class NotificationsTab extends React.Component {
|
||||
this.setState({channelKey: val});
|
||||
}
|
||||
updateCustomMentionKeys() {
|
||||
var checked = React.findDOMNode(this.refs.customcheck).checked;
|
||||
var checked = ReactDOM.findDOMNode(this.refs.customcheck).checked;
|
||||
|
||||
if (checked) {
|
||||
var text = React.findDOMNode(this.refs.custommentions).value;
|
||||
var text = ReactDOM.findDOMNode(this.refs.custommentions).value;
|
||||
|
||||
// remove all spaces and split string into individual keys
|
||||
this.setState({customKeys: text.replace(/ /g, ''), customKeysChecked: true});
|
||||
@@ -196,7 +196,7 @@ export default class NotificationsTab extends React.Component {
|
||||
}
|
||||
}
|
||||
onCustomChange() {
|
||||
React.findDOMNode(this.refs.customcheck).checked = true;
|
||||
ReactDOM.findDOMNode(this.refs.customcheck).checked = true;
|
||||
this.updateCustomMentionKeys();
|
||||
}
|
||||
render() {
|
||||
|
||||
@@ -82,7 +82,7 @@ export default class SecurityTab extends React.Component {
|
||||
$('#user_settings').modal('hide');
|
||||
}
|
||||
handleClose() {
|
||||
$(React.findDOMNode(this)).find('.form-control').each(function resetValue() {
|
||||
$(ReactDOM.findDOMNode(this)).find('.form-control').each(function resetValue() {
|
||||
this.value = '';
|
||||
});
|
||||
this.setState({currentPassword: '', newPassword: '', confirmPassword: '', serverError: null, passwordError: null});
|
||||
|
||||
@@ -77,7 +77,7 @@ export default class ViewImageModal extends React.Component {
|
||||
}
|
||||
onModalHidden() {
|
||||
if (this.refs.video) {
|
||||
var video = React.findDOMNode(this.refs.video);
|
||||
var video = ReactDOM.findDOMNode(this.refs.video);
|
||||
video.pause();
|
||||
video.currentTime = 0;
|
||||
}
|
||||
|
||||
@@ -6,17 +6,17 @@ var SelectTeamModal = require('../components/admin_console/select_team_modal.jsx
|
||||
var AdminController = require('../components/admin_console/admin_controller.jsx');
|
||||
|
||||
export function setupAdminConsolePage() {
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<AdminController />,
|
||||
document.getElementById('admin_controller')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<SelectTeamModal />,
|
||||
document.getElementById('select_team_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<ErrorBar/>,
|
||||
document.getElementById('error_bar')
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
var Authorize = require('../components/authorize.jsx');
|
||||
|
||||
function setupAuthorizePage(props) {
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<Authorize
|
||||
teamName={props.TeamName}
|
||||
appName={props.AppName}
|
||||
|
||||
@@ -24,7 +24,6 @@ var TeamSettingsModal = require('../components/team_settings_modal.jsx');
|
||||
var ChannelMembersModal = require('../components/channel_members.jsx');
|
||||
var ChannelInviteModal = require('../components/channel_invite_modal.jsx');
|
||||
var TeamMembersModal = require('../components/team_members.jsx');
|
||||
var DirectChannelModal = require('../components/more_direct_channels.jsx');
|
||||
var ErrorBar = require('../components/error_bar.jsx');
|
||||
var ErrorStore = require('../stores/error_store.jsx');
|
||||
var ChannelLoader = require('../components/channel_loader.jsx');
|
||||
@@ -56,22 +55,22 @@ function setupChannelPage(props) {
|
||||
});
|
||||
|
||||
// ChannelLoader must be rendered first
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<ChannelLoader/>,
|
||||
document.getElementById('channel_loader')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<ErrorBar/>,
|
||||
document.getElementById('error_bar')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<Navbar teamDisplayName={props.TeamDisplayName} />,
|
||||
document.getElementById('navbar')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<Sidebar
|
||||
teamDisplayName={props.TeamDisplayName}
|
||||
teamName={props.TeamName}
|
||||
@@ -80,117 +79,112 @@ function setupChannelPage(props) {
|
||||
document.getElementById('sidebar-left')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<GetLinkModal />,
|
||||
document.getElementById('get_link_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<UserSettingsModal />,
|
||||
document.getElementById('user_settings_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<ImportThemeModal />,
|
||||
document.getElementById('import_theme_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<TeamSettingsModal teamDisplayName={props.TeamDisplayName} />,
|
||||
document.getElementById('team_settings_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<TeamMembersModal teamDisplayName={props.TeamDisplayName} />,
|
||||
document.getElementById('team_members_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<MemberInviteModal teamType={props.TeamType} />,
|
||||
document.getElementById('invite_member_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<ChannelHeader />,
|
||||
document.getElementById('channel-header')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<EditChannelModal />,
|
||||
document.getElementById('edit_channel_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<DeleteChannelModal />,
|
||||
document.getElementById('delete_channel_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<RenameChannelModal />,
|
||||
document.getElementById('rename_channel_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<ChannelNotificationsModal />,
|
||||
document.getElementById('channel_notifications_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<ChannelMembersModal />,
|
||||
document.getElementById('channel_members_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<ChannelInviteModal />,
|
||||
document.getElementById('channel_invite_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<ChannelInfoModal />,
|
||||
document.getElementById('channel_info_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<MoreChannelsModal />,
|
||||
document.getElementById('more_channels_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
<DirectChannelModal />,
|
||||
document.getElementById('direct_channel_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<PostListContainer />,
|
||||
document.getElementById('post-list')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<EditPostModal />,
|
||||
document.getElementById('edit_post_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<DeletePostModal />,
|
||||
document.getElementById('delete_post_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<PostDeletedModal />,
|
||||
document.getElementById('post_deleted_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<CreatePost />,
|
||||
document.getElementById('post-create')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<SidebarRight />,
|
||||
document.getElementById('sidebar-right')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<SidebarRightMenu
|
||||
teamDisplayName={props.TeamDisplayName}
|
||||
teamType={props.TeamType}
|
||||
@@ -198,44 +192,44 @@ function setupChannelPage(props) {
|
||||
document.getElementById('sidebar-menu')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<MentionList id='post_textbox' />,
|
||||
document.getElementById('post_mention_tab')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<MentionList id='reply_textbox' />,
|
||||
document.getElementById('reply_mention_tab')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<MentionList id='edit_textbox' />,
|
||||
document.getElementById('edit_mention_tab')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<AccessHistoryModal />,
|
||||
document.getElementById('access_history_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<ActivityLogModal />,
|
||||
document.getElementById('activity_log_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<RemovedFromChannelModal />,
|
||||
document.getElementById('removed_from_channel_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<FileUploadOverlay
|
||||
overlayType='center'
|
||||
/>,
|
||||
document.getElementById('file_upload_overlay')
|
||||
);
|
||||
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<RegisterAppModal />,
|
||||
document.getElementById('register_app_modal')
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
var FindTeam = require('../components/find_team.jsx');
|
||||
|
||||
function setupFindTeamPage() {
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<FindTeam />,
|
||||
document.getElementById('find-team')
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
var Login = require('../components/login.jsx');
|
||||
|
||||
function setupLoginPage(props) {
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<Login
|
||||
teamDisplayName={props.TeamDisplayName}
|
||||
teamName={props.TeamName}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
var PasswordReset = require('../components/password_reset.jsx');
|
||||
|
||||
function setupPasswordResetPage(props) {
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<PasswordReset
|
||||
isReset={props.IsReset}
|
||||
teamDisplayName={props.TeamDisplayName}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
var SignupTeam = require('../components/signup_team.jsx');
|
||||
|
||||
function setupSignupTeamPage() {
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<SignupTeam />,
|
||||
document.getElementById('signup-team')
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
var SignupTeamComplete = require('../components/signup_team_complete.jsx');
|
||||
|
||||
function setupSignupTeamCompletePage(props) {
|
||||
React.render(
|
||||
ReactDOM.render(
|
||||
<SignupTeamComplete
|
||||
email={props.Email}
|
||||
hash={props.Hash}
|
||||
|
||||
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Ссылка в новой задаче
Block a user