mirror of
https://github.com/mattermost/focalboard.git
synced 2025-03-29 21:01:01 +02:00
Merge pull request #4684 from mattermost/main
Sync release-7.10 with main
This commit is contained in:
commit
f7156b6341
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@ -15,7 +15,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
ci-ubuntu-server:
|
ci-ubuntu-server:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@ -44,7 +44,7 @@ jobs:
|
|||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref : "master"
|
ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1"
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
@ -54,7 +54,7 @@ jobs:
|
|||||||
run: cd focalboard; make server-test-${{matrix['db']}}
|
run: cd focalboard; make server-test-${{matrix['db']}}
|
||||||
|
|
||||||
ci-ubuntu-webapp:
|
ci-ubuntu-webapp:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@ -74,7 +74,7 @@ jobs:
|
|||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref : "master"
|
ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1"
|
||||||
- name: npm ci
|
- name: npm ci
|
||||||
run: |
|
run: |
|
||||||
cd focalboard/webapp && npm ci && cd -
|
cd focalboard/webapp && npm ci && cd -
|
||||||
@ -132,7 +132,7 @@ jobs:
|
|||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref : "master"
|
ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1"
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
@ -169,7 +169,7 @@ jobs:
|
|||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref : "master"
|
ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1"
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
|
31
.github/workflows/dev-release.yml
vendored
31
.github/workflows/dev-release.yml
vendored
@ -8,14 +8,13 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||||
EXCLUDE_ENTERPRISE: true
|
EXCLUDE_ENTERPRISE: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
ubuntu:
|
ubuntu:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
@ -25,16 +24,16 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref: ${{ env.BRANCH_NAME }}
|
ref: ${{ env.BRANCH_NAME }}
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
if: steps.mattermostServer.outcome == 'failure'
|
if: steps.mattermostServer.outcome == 'failure'
|
||||||
with:
|
with:
|
||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref : "master"
|
ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1"
|
||||||
|
|
||||||
- name: Replace token 1 server
|
- name: Replace token 1 server
|
||||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go
|
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go
|
||||||
@ -101,16 +100,16 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref: ${{ env.BRANCH_NAME }}
|
ref: ${{ env.BRANCH_NAME }}
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
if: steps.mattermostServer.outcome == 'failure'
|
if: steps.mattermostServer.outcome == 'failure'
|
||||||
with:
|
with:
|
||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref : "master"
|
ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1"
|
||||||
- name: Replace token 1 server
|
- name: Replace token 1 server
|
||||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go
|
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go
|
||||||
|
|
||||||
@ -159,16 +158,16 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref: ${{ env.BRANCH_NAME }}
|
ref: ${{ env.BRANCH_NAME }}
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
if: steps.mattermostServer.outcome == 'failure'
|
if: steps.mattermostServer.outcome == 'failure'
|
||||||
with:
|
with:
|
||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref : "master"
|
ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1"
|
||||||
- name: Replace token 1 server
|
- name: Replace token 1 server
|
||||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go
|
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go
|
||||||
|
|
||||||
@ -218,7 +217,7 @@ jobs:
|
|||||||
path: ${{ github.workspace }}/focalboard/win-wpf/dist/focalboard-win.zip
|
path: ${{ github.workspace }}/focalboard/win-wpf/dist/focalboard-win.zip
|
||||||
|
|
||||||
plugin:
|
plugin:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@ -229,16 +228,16 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref: ${{ env.BRANCH_NAME }}
|
ref: ${{ env.BRANCH_NAME }}
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
if: steps.mattermostServer.outcome == 'failure'
|
if: steps.mattermostServer.outcome == 'failure'
|
||||||
with:
|
with:
|
||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref : "master"
|
ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1"
|
||||||
|
|
||||||
- name: Replace token 1 server
|
- name: Replace token 1 server
|
||||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go
|
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go
|
||||||
|
6
.github/workflows/lint-server.yml
vendored
6
.github/workflows/lint-server.yml
vendored
@ -13,7 +13,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
down-migrations:
|
down-migrations:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
@ -26,7 +26,7 @@ jobs:
|
|||||||
|
|
||||||
golangci:
|
golangci:
|
||||||
name: plugin
|
name: plugin
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-go@v3
|
- uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
@ -48,7 +48,7 @@ jobs:
|
|||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref : "master"
|
ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1"
|
||||||
- name: set up golangci-lint
|
- name: set up golangci-lint
|
||||||
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.50.1
|
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.50.1
|
||||||
- name: lint
|
- name: lint
|
||||||
|
28
.github/workflows/prod-release.yml
vendored
28
.github/workflows/prod-release.yml
vendored
@ -9,7 +9,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
ubuntu:
|
ubuntu:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@ -21,16 +21,16 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref: ${{ env.BRANCH_NAME }}
|
ref: ${{ env.BRANCH_NAME }}
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
if: steps.mattermostServer.outcome == 'failure'
|
if: steps.mattermostServer.outcome == 'failure'
|
||||||
with:
|
with:
|
||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref : "master"
|
ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1"
|
||||||
|
|
||||||
- name: Replace token 1 server
|
- name: Replace token 1 server
|
||||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go
|
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go
|
||||||
@ -97,16 +97,16 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref: ${{ env.BRANCH_NAME }}
|
ref: ${{ env.BRANCH_NAME }}
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
if: steps.mattermostServer.outcome == 'failure'
|
if: steps.mattermostServer.outcome == 'failure'
|
||||||
with:
|
with:
|
||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref : "master"
|
ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1"
|
||||||
|
|
||||||
- name: Replace token 1 server
|
- name: Replace token 1 server
|
||||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go
|
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go
|
||||||
@ -156,16 +156,16 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref: ${{ env.BRANCH_NAME }}
|
ref: ${{ env.BRANCH_NAME }}
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
if: steps.mattermostServer.outcome == 'failure'
|
if: steps.mattermostServer.outcome == 'failure'
|
||||||
with:
|
with:
|
||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref : "master"
|
ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1"
|
||||||
|
|
||||||
- name: Replace token 1 server
|
- name: Replace token 1 server
|
||||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go
|
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go
|
||||||
@ -216,7 +216,7 @@ jobs:
|
|||||||
path: ${{ github.workspace }}/focalboard/win-wpf/dist/focalboard-win.zip
|
path: ${{ github.workspace }}/focalboard/win-wpf/dist/focalboard-win.zip
|
||||||
|
|
||||||
plugin-release:
|
plugin-release:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@ -228,16 +228,16 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref: ${{ env.BRANCH_NAME }}
|
ref: ${{ env.BRANCH_NAME }}
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
if: steps.mattermostServer.outcome == 'failure'
|
if: steps.mattermostServer.outcome == 'failure'
|
||||||
with:
|
with:
|
||||||
repository: "mattermost/mattermost-server"
|
repository: "mattermost/mattermost-server"
|
||||||
fetch-depth: "20"
|
fetch-depth: "20"
|
||||||
path: "mattermost-server"
|
path: "mattermost-server"
|
||||||
ref : "master"
|
ref : "b61c096497ac1f22f64b77afe58d0dd5a72b38f1"
|
||||||
|
|
||||||
- name: Replace token 1 server
|
- name: Replace token 1 server
|
||||||
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go
|
run: sed -i -e "s,placeholder_rudder_dataplane_url,${{ secrets.RUDDER_DATAPLANE_URL }},g" ${{ github.workspace }}/focalboard/server/services/telemetry/telemetry.go
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -20,9 +22,30 @@ import (
|
|||||||
mmModel "github.com/mattermost/mattermost-server/v6/model"
|
mmModel "github.com/mattermost/mattermost-server/v6/model"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/web"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var UnsafeContentTypes = [...]string{
|
||||||
|
"application/javascript",
|
||||||
|
"application/ecmascript",
|
||||||
|
"text/javascript",
|
||||||
|
"text/ecmascript",
|
||||||
|
"application/x-javascript",
|
||||||
|
"text/html",
|
||||||
|
}
|
||||||
|
|
||||||
|
var MediaContentTypes = [...]string{
|
||||||
|
"image/jpeg",
|
||||||
|
"image/png",
|
||||||
|
"image/bmp",
|
||||||
|
"image/gif",
|
||||||
|
"image/tiff",
|
||||||
|
"video/avi",
|
||||||
|
"video/mpeg",
|
||||||
|
"video/mp4",
|
||||||
|
"audio/mpeg",
|
||||||
|
"audio/wav",
|
||||||
|
}
|
||||||
|
|
||||||
// FileUploadResponse is the response to a file upload
|
// FileUploadResponse is the response to a file upload
|
||||||
// swagger:model
|
// swagger:model
|
||||||
type FileUploadResponse struct {
|
type FileUploadResponse struct {
|
||||||
@ -145,10 +168,74 @@ func (a *API) handleServeFile(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defer fileReader.Close()
|
defer fileReader.Close()
|
||||||
web.WriteFileResponse(filename, fileInfo.MimeType, fileInfo.Size, time.Now(), "", fileReader, false, w, r)
|
mimeType := ""
|
||||||
|
var fileSize int64
|
||||||
|
if fileInfo != nil {
|
||||||
|
mimeType = fileInfo.MimeType
|
||||||
|
fileSize = fileInfo.Size
|
||||||
|
}
|
||||||
|
writeFileResponse(filename, mimeType, fileSize, time.Now(), "", fileReader, false, w, r)
|
||||||
auditRec.Success()
|
auditRec.Success()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeFileResponse(filename string, contentType string, contentSize int64,
|
||||||
|
lastModification time.Time, webserverMode string, fileReader io.ReadSeeker, forceDownload bool, w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Cache-Control", "private, no-cache")
|
||||||
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
|
|
||||||
|
if contentSize > 0 {
|
||||||
|
contentSizeStr := strconv.Itoa(int(contentSize))
|
||||||
|
if webserverMode == "gzip" {
|
||||||
|
w.Header().Set("X-Uncompressed-Content-Length", contentSizeStr)
|
||||||
|
} else {
|
||||||
|
w.Header().Set("Content-Length", contentSizeStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if contentType == "" {
|
||||||
|
contentType = "application/octet-stream"
|
||||||
|
} else {
|
||||||
|
for _, unsafeContentType := range UnsafeContentTypes {
|
||||||
|
if strings.HasPrefix(contentType, unsafeContentType) {
|
||||||
|
contentType = "text/plain"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", contentType)
|
||||||
|
|
||||||
|
var toDownload bool
|
||||||
|
if forceDownload {
|
||||||
|
toDownload = true
|
||||||
|
} else {
|
||||||
|
isMediaType := false
|
||||||
|
|
||||||
|
for _, mediaContentType := range MediaContentTypes {
|
||||||
|
if strings.HasPrefix(contentType, mediaContentType) {
|
||||||
|
isMediaType = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toDownload = !isMediaType
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = url.PathEscape(filename)
|
||||||
|
|
||||||
|
if toDownload {
|
||||||
|
w.Header().Set("Content-Disposition", "attachment;filename=\""+filename+"\"; filename*=UTF-8''"+filename)
|
||||||
|
} else {
|
||||||
|
w.Header().Set("Content-Disposition", "inline;filename=\""+filename+"\"; filename*=UTF-8''"+filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevent file links from being embedded in iframes
|
||||||
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
|
w.Header().Set("Content-Security-Policy", "Frame-ancestors 'none'")
|
||||||
|
|
||||||
|
http.ServeContent(w, r, filename, lastModification, fileReader)
|
||||||
|
}
|
||||||
|
|
||||||
func (a *API) getFileInfo(w http.ResponseWriter, r *http.Request) {
|
func (a *API) getFileInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
// swagger:operation GET /files/teams/{teamID}/{boardID}/{filename}/info getFile
|
// swagger:operation GET /files/teams/{teamID}/{boardID}/{filename}/info getFile
|
||||||
//
|
//
|
||||||
|
@ -868,10 +868,8 @@ func (s *SQLStore) doesDuplicateCategoryBoardsExist() (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) runMySQLDeDuplicateCategoryBoardsMigration() error {
|
func (s *SQLStore) runMySQLDeDuplicateCategoryBoardsMigration() error {
|
||||||
query := "WITH duplicates AS (SELECT id, ROW_NUMBER() OVER(PARTITION BY user_id, board_id) AS rownum " +
|
query := "DELETE FROM " + s.tablePrefix + "category_boards WHERE id NOT IN " +
|
||||||
"FROM " + s.tablePrefix + "category_boards) " +
|
"(SELECT * FROM ( SELECT min(id) FROM " + s.tablePrefix + "category_boards GROUP BY user_id, board_id ) as data)"
|
||||||
"DELETE " + s.tablePrefix + "category_boards FROM " + s.tablePrefix + "category_boards " +
|
|
||||||
"JOIN duplicates USING(id) WHERE duplicates.rownum > 1;"
|
|
||||||
if _, err := s.db.Exec(query); err != nil {
|
if _, err := s.db.Exec(query); err != nil {
|
||||||
s.logger.Error("Failed to de-duplicate data in category_boards table", mlog.Err(err))
|
s.logger.Error("Failed to de-duplicate data in category_boards table", mlog.Err(err))
|
||||||
}
|
}
|
||||||
|
@ -319,7 +319,7 @@ func (s *SQLStore) GetTemplateHelperFuncs() template.FuncMap {
|
|||||||
|
|
||||||
func (s *SQLStore) genAddColumnIfNeeded(tableName, columnName, datatype, constraint string) (string, error) {
|
func (s *SQLStore) genAddColumnIfNeeded(tableName, columnName, datatype, constraint string) (string, error) {
|
||||||
tableName = addPrefixIfNeeded(tableName, s.tablePrefix)
|
tableName = addPrefixIfNeeded(tableName, s.tablePrefix)
|
||||||
normTableName := normalizeTablename(s.schemaName, tableName)
|
normTableName := s.normalizeTablename(tableName)
|
||||||
|
|
||||||
switch s.dbType {
|
switch s.dbType {
|
||||||
case model.SqliteDBType:
|
case model.SqliteDBType:
|
||||||
@ -358,7 +358,7 @@ func (s *SQLStore) genAddColumnIfNeeded(tableName, columnName, datatype, constra
|
|||||||
|
|
||||||
func (s *SQLStore) genDropColumnIfNeeded(tableName, columnName string) (string, error) {
|
func (s *SQLStore) genDropColumnIfNeeded(tableName, columnName string) (string, error) {
|
||||||
tableName = addPrefixIfNeeded(tableName, s.tablePrefix)
|
tableName = addPrefixIfNeeded(tableName, s.tablePrefix)
|
||||||
normTableName := normalizeTablename(s.schemaName, tableName)
|
normTableName := s.normalizeTablename(tableName)
|
||||||
|
|
||||||
switch s.dbType {
|
switch s.dbType {
|
||||||
case model.SqliteDBType:
|
case model.SqliteDBType:
|
||||||
@ -395,7 +395,7 @@ func (s *SQLStore) genDropColumnIfNeeded(tableName, columnName string) (string,
|
|||||||
func (s *SQLStore) genCreateIndexIfNeeded(tableName, columns string) (string, error) {
|
func (s *SQLStore) genCreateIndexIfNeeded(tableName, columns string) (string, error) {
|
||||||
indexName := getIndexName(tableName, columns)
|
indexName := getIndexName(tableName, columns)
|
||||||
tableName = addPrefixIfNeeded(tableName, s.tablePrefix)
|
tableName = addPrefixIfNeeded(tableName, s.tablePrefix)
|
||||||
normTableName := normalizeTablename(s.schemaName, tableName)
|
normTableName := s.normalizeTablename(tableName)
|
||||||
|
|
||||||
switch s.dbType {
|
switch s.dbType {
|
||||||
case model.SqliteDBType:
|
case model.SqliteDBType:
|
||||||
@ -435,7 +435,7 @@ func (s *SQLStore) genRenameTableIfNeeded(oldTableName, newTableName string) (st
|
|||||||
oldTableName = addPrefixIfNeeded(oldTableName, s.tablePrefix)
|
oldTableName = addPrefixIfNeeded(oldTableName, s.tablePrefix)
|
||||||
newTableName = addPrefixIfNeeded(newTableName, s.tablePrefix)
|
newTableName = addPrefixIfNeeded(newTableName, s.tablePrefix)
|
||||||
|
|
||||||
normOldTableName := normalizeTablename(s.schemaName, oldTableName)
|
normOldTableName := s.normalizeTablename(oldTableName)
|
||||||
|
|
||||||
vars := map[string]string{
|
vars := map[string]string{
|
||||||
"schema": s.schemaName,
|
"schema": s.schemaName,
|
||||||
@ -466,14 +466,14 @@ func (s *SQLStore) genRenameTableIfNeeded(oldTableName, newTableName string) (st
|
|||||||
case model.PostgresDBType:
|
case model.PostgresDBType:
|
||||||
return replaceVars(`
|
return replaceVars(`
|
||||||
do $$
|
do $$
|
||||||
begin
|
begin
|
||||||
if (SELECT COUNT(table_name) FROM INFORMATION_SCHEMA.TABLES
|
if (SELECT COUNT(table_name) FROM INFORMATION_SCHEMA.TABLES
|
||||||
WHERE table_name = '[[new_table_name]]'
|
WHERE table_name = '[[new_table_name]]'
|
||||||
AND table_schema = '[[schema]]'
|
AND table_schema = '[[schema]]'
|
||||||
) = 0 then
|
) = 0 then
|
||||||
ALTER TABLE [[norm_old_table_name]] RENAME TO [[new_table_name]];
|
ALTER TABLE [[norm_old_table_name]] RENAME TO [[new_table_name]];
|
||||||
end if;
|
end if;
|
||||||
end$$;
|
end$$;
|
||||||
`, vars), nil
|
`, vars), nil
|
||||||
default:
|
default:
|
||||||
return "", ErrUnsupportedDatabaseType
|
return "", ErrUnsupportedDatabaseType
|
||||||
@ -482,7 +482,7 @@ func (s *SQLStore) genRenameTableIfNeeded(oldTableName, newTableName string) (st
|
|||||||
|
|
||||||
func (s *SQLStore) genRenameColumnIfNeeded(tableName, oldColumnName, newColumnName, dataType string) (string, error) {
|
func (s *SQLStore) genRenameColumnIfNeeded(tableName, oldColumnName, newColumnName, dataType string) (string, error) {
|
||||||
tableName = addPrefixIfNeeded(tableName, s.tablePrefix)
|
tableName = addPrefixIfNeeded(tableName, s.tablePrefix)
|
||||||
normTableName := normalizeTablename(s.schemaName, tableName)
|
normTableName := s.normalizeTablename(tableName)
|
||||||
|
|
||||||
vars := map[string]string{
|
vars := map[string]string{
|
||||||
"schema": s.schemaName,
|
"schema": s.schemaName,
|
||||||
@ -516,15 +516,15 @@ func (s *SQLStore) genRenameColumnIfNeeded(tableName, oldColumnName, newColumnNa
|
|||||||
case model.PostgresDBType:
|
case model.PostgresDBType:
|
||||||
return replaceVars(`
|
return replaceVars(`
|
||||||
do $$
|
do $$
|
||||||
begin
|
begin
|
||||||
if (SELECT COUNT(table_name) FROM INFORMATION_SCHEMA.COLUMNS
|
if (SELECT COUNT(table_name) FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
WHERE table_name = '[[table_name]]'
|
WHERE table_name = '[[table_name]]'
|
||||||
AND table_schema = '[[schema]]'
|
AND table_schema = '[[schema]]'
|
||||||
AND column_name = '[[new_column_name]]'
|
AND column_name = '[[new_column_name]]'
|
||||||
) = 0 then
|
) = 0 then
|
||||||
ALTER TABLE [[norm_table_name]] RENAME COLUMN [[old_column_name]] TO [[new_column_name]];
|
ALTER TABLE [[norm_table_name]] RENAME COLUMN [[old_column_name]] TO [[new_column_name]];
|
||||||
end if;
|
end if;
|
||||||
end$$;
|
end$$;
|
||||||
`, vars), nil
|
`, vars), nil
|
||||||
default:
|
default:
|
||||||
return "", ErrUnsupportedDatabaseType
|
return "", ErrUnsupportedDatabaseType
|
||||||
@ -620,7 +620,7 @@ func (s *SQLStore) doesColumnExist(tableName, columnName string) (bool, error) {
|
|||||||
|
|
||||||
func (s *SQLStore) genAddConstraintIfNeeded(tableName, constraintName, constraintType, constraintDefinition string) (string, error) {
|
func (s *SQLStore) genAddConstraintIfNeeded(tableName, constraintName, constraintType, constraintDefinition string) (string, error) {
|
||||||
tableName = addPrefixIfNeeded(tableName, s.tablePrefix)
|
tableName = addPrefixIfNeeded(tableName, s.tablePrefix)
|
||||||
normTableName := normalizeTablename(s.schemaName, tableName)
|
normTableName := s.normalizeTablename(tableName)
|
||||||
|
|
||||||
var query string
|
var query string
|
||||||
|
|
||||||
@ -686,8 +686,12 @@ func addPrefixIfNeeded(s, prefix string) string {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeTablename(schemaName, tableName string) string {
|
func (s *SQLStore) normalizeTablename(tableName string) string {
|
||||||
if schemaName != "" && !strings.HasPrefix(tableName, schemaName+".") {
|
if s.schemaName != "" && !strings.HasPrefix(tableName, s.schemaName+".") {
|
||||||
|
schemaName := s.schemaName
|
||||||
|
if s.dbType == model.MysqlDBType {
|
||||||
|
schemaName = "`" + schemaName + "`"
|
||||||
|
}
|
||||||
tableName = schemaName + "." + tableName
|
tableName = schemaName + "." + tableName
|
||||||
}
|
}
|
||||||
return tableName
|
return tableName
|
||||||
|
@ -246,6 +246,9 @@ func (bm *BoardsMigrator) MigrateToStep(step int) error {
|
|||||||
func (bm *BoardsMigrator) Interceptors() map[int]foundation.Interceptor {
|
func (bm *BoardsMigrator) Interceptors() map[int]foundation.Interceptor {
|
||||||
return map[int]foundation.Interceptor{
|
return map[int]foundation.Interceptor{
|
||||||
18: bm.store.RunDeletedMembershipBoardsMigration,
|
18: bm.store.RunDeletedMembershipBoardsMigration,
|
||||||
|
35: func() error {
|
||||||
|
return bm.store.RunDeDuplicateCategoryBoardsMigration(35)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package migrationstests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRunDeDuplicateCategoryBoardsMigration(t *testing.T) {
|
||||||
|
th, tearDown := SetupTestHelper(t)
|
||||||
|
defer tearDown()
|
||||||
|
|
||||||
|
if th.IsSQLite() {
|
||||||
|
t.Skip("SQLite is not supported for this")
|
||||||
|
}
|
||||||
|
|
||||||
|
th.f.MigrateToStepSkippingLastInterceptor(35).
|
||||||
|
ExecFile("./fixtures/testDeDuplicateCategoryBoardsMigration.sql")
|
||||||
|
|
||||||
|
th.f.RunInterceptor(35)
|
||||||
|
|
||||||
|
// verifying count of rows
|
||||||
|
var count int
|
||||||
|
countQuery := "SELECT COUNT(*) FROM focalboard_category_boards"
|
||||||
|
row := th.f.DB().QueryRow(countQuery)
|
||||||
|
err := row.Scan(&count)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 4, count)
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
INSERT INTO focalboard_category_boards values
|
INSERT INTO focalboard_category_boards
|
||||||
|
(id, user_id, category_id, board_id, create_at, update_at, delete_at, sort_order)
|
||||||
|
values
|
||||||
('id-1', 'user_id-1', 'category-id-1', 'board-id-1', 1672988834402, 1672988834402, 0, 0),
|
('id-1', 'user_id-1', 'category-id-1', 'board-id-1', 1672988834402, 1672988834402, 0, 0),
|
||||||
('id-2', 'user_id-1', 'category-id-2', 'board-id-1', 1672988834402, 1672988834402, 0, 0),
|
('id-2', 'user_id-1', 'category-id-2', 'board-id-1', 1672988834402, 1672988834402, 0, 0),
|
||||||
('id-3', 'user_id-2', 'category-id-3', 'board-id-2', 1672988834402, 1672988834402, 1672988834402, 0),
|
('id-3', 'user_id-2', 'category-id-3', 'board-id-2', 1672988834402, 1672988834402, 1672988834402, 0),
|
||||||
('id-4', 'user_id-2', 'category-id-3', 'board-id-4', 1672988834402, 1672988834402, 0, 0),
|
('id-4', 'user_id-2', 'category-id-3', 'board-id-4', 1672988834402, 1672988834402, 0, 0),
|
||||||
('id-5', 'user_id-3', 'category-id-4', 'board-id-3', 1672988834402, 1672988834402, 1672988834402, 0);
|
('id-5', 'user_id-3', 'category-id-4', 'board-id-3', 1672988834402, 1672988834402, 1672988834402, 0);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
INSERT INTO focalboard_category_boards values
|
INSERT INTO focalboard_category_boards
|
||||||
|
(id, user_id, category_id, board_id, create_at, update_at, delete_at, sort_order)
|
||||||
|
values
|
||||||
('id-1', 'user_id-1', 'category-id-1', 'board-id-1', 1672988834402, 1672988834402, 0, 0),
|
('id-1', 'user_id-1', 'category-id-1', 'board-id-1', 1672988834402, 1672988834402, 0, 0),
|
||||||
('id-2', 'user_id-1', 'category-id-2', 'board-id-1', 1672988834402, 1672988834402, 0, 0),
|
('id-2', 'user_id-1', 'category-id-2', 'board-id-1', 1672988834402, 1672988834402, 0, 0),
|
||||||
('id-3', 'user_id-2', 'category-id-3', 'board-id-2', 1672988834402, 1672988834402, 0, 0),
|
('id-3', 'user_id-2', 'category-id-3', 'board-id-2', 1672988834402, 1672988834402, 0, 0),
|
||||||
('id-4', 'user_id-2', 'category-id-3', 'board-id-4', 1672988834402, 1672988834402, 0, 0),
|
('id-4', 'user_id-2', 'category-id-3', 'board-id-4', 1672988834402, 1672988834402, 0, 0),
|
||||||
('id-5', 'user_id-3', 'category-id-4', 'board-id-3', 1672988834402, 1672988834402, 0, 0);
|
('id-5', 'user_id-3', 'category-id-4', 'board-id-3', 1672988834402, 1672988834402, 0, 0);
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
INSERT INTO focalboard_category_boards VALUES
|
INSERT INTO focalboard_category_boards
|
||||||
|
(id, user_id, category_id, board_id, create_at, update_at, sort_order, hidden)
|
||||||
|
VALUES
|
||||||
('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false),
|
('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false),
|
||||||
('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false),
|
('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false),
|
||||||
('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false),
|
('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false),
|
||||||
@ -7,4 +9,4 @@ INSERT INTO focalboard_category_boards VALUES
|
|||||||
|
|
||||||
INSERT INTO Preferences VALUES
|
INSERT INTO Preferences VALUES
|
||||||
('user-id-1', 'focalboard', 'hiddenBoardIDs', '["board-id-1"]'),
|
('user-id-1', 'focalboard', 'hiddenBoardIDs', '["board-id-1"]'),
|
||||||
('user-id-2', 'focalboard', 'hiddenBoardIDs', '["board-id-3", "board-id-4"]');
|
('user-id-2', 'focalboard', 'hiddenBoardIDs', '["board-id-3", "board-id-4"]');
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
INSERT INTO focalboard_category_boards VALUES
|
INSERT INTO focalboard_category_boards
|
||||||
|
(id, user_id, category_id, board_id, create_at, update_at, sort_order, hidden)
|
||||||
|
VALUES
|
||||||
('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false),
|
('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false),
|
||||||
('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false),
|
('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false),
|
||||||
('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false),
|
('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false),
|
||||||
('id-4', 'user-id-2', 'category-id-3', 'board-id-4', 1672889246832, 1672889246832, 0, false),
|
('id-4', 'user-id-2', 'category-id-3', 'board-id-4', 1672889246832, 1672889246832, 0, false),
|
||||||
('id-5', 'user-id-3', 'category-id-4', 'board-id-5', 1672889246832, 1672889246832, 0, false);
|
('id-5', 'user-id-3', 'category-id-4', 'board-id-5', 1672889246832, 1672889246832, 0, false);
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
INSERT INTO focalboard_category_boards VALUES
|
INSERT INTO focalboard_category_boards
|
||||||
|
(id, user_id, category_id, board_id, create_at, update_at, sort_order, hidden)
|
||||||
|
VALUES
|
||||||
('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false),
|
('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false),
|
||||||
('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false),
|
('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false),
|
||||||
('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false),
|
('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false),
|
||||||
@ -7,4 +9,4 @@ INSERT INTO focalboard_category_boards VALUES
|
|||||||
|
|
||||||
INSERT INTO Preferences VALUES
|
INSERT INTO Preferences VALUES
|
||||||
('user-id-1', 'focalboard', 'hiddenBoardIDs', ''),
|
('user-id-1', 'focalboard', 'hiddenBoardIDs', ''),
|
||||||
('user-id-2', 'focalboard', 'hiddenBoardIDs', '');
|
('user-id-2', 'focalboard', 'hiddenBoardIDs', '');
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
INSERT INTO focalboard_category_boards VALUES
|
INSERT INTO focalboard_category_boards
|
||||||
|
(id, user_id, category_id, board_id, create_at, update_at, sort_order, hidden)
|
||||||
|
VALUES
|
||||||
('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false),
|
('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false),
|
||||||
('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false),
|
('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false),
|
||||||
('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false),
|
('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false),
|
||||||
@ -7,4 +9,4 @@ INSERT INTO focalboard_category_boards VALUES
|
|||||||
|
|
||||||
INSERT INTO focalboard_preferences VALUES
|
INSERT INTO focalboard_preferences VALUES
|
||||||
('user-id-1', 'focalboard', 'hiddenBoardIDs', '["board-id-1"]'),
|
('user-id-1', 'focalboard', 'hiddenBoardIDs', '["board-id-1"]'),
|
||||||
('user-id-2', 'focalboard', 'hiddenBoardIDs', '["board-id-3", "board-id-4"]');
|
('user-id-2', 'focalboard', 'hiddenBoardIDs', '["board-id-3", "board-id-4"]');
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
INSERT INTO focalboard_category_boards VALUES
|
INSERT INTO focalboard_category_boards
|
||||||
|
(id, user_id, category_id, board_id, create_at, update_at, sort_order, hidden)
|
||||||
|
VALUES
|
||||||
('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false),
|
('id-1', 'user-id-1', 'category-id-1', 'board-id-1', 1672889246832, 1672889246832, 0, false),
|
||||||
('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false),
|
('id-2', 'user-id-1', 'category-id-2', 'board-id-2', 1672889246832, 1672889246832, 0, false),
|
||||||
('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false),
|
('id-3', 'user-id-2', 'category-id-3', 'board-id-3', 1672889246832, 1672889246832, 0, false),
|
||||||
@ -7,4 +9,4 @@ INSERT INTO focalboard_category_boards VALUES
|
|||||||
|
|
||||||
INSERT INTO focalboard_preferences VALUES
|
INSERT INTO focalboard_preferences VALUES
|
||||||
('user-id-1', 'focalboard', 'hiddenBoardIDs', ''),
|
('user-id-1', 'focalboard', 'hiddenBoardIDs', ''),
|
||||||
('user-id-2', 'focalboard', 'hiddenBoardIDs', '');
|
('user-id-2', 'focalboard', 'hiddenBoardIDs', '');
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
INSERT INTO focalboard_category_boards(id, user_id, category_id, board_id, create_at, update_at, sort_order)
|
||||||
|
VALUES
|
||||||
|
('id_1', 'user_id_1', 'category_id_1', 'board_id_1', 0, 0, 0),
|
||||||
|
('id_2', 'user_id_1', 'category_id_2', 'board_id_1', 0, 0, 0),
|
||||||
|
('id_3', 'user_id_1', 'category_id_3', 'board_id_1', 0, 0, 0),
|
||||||
|
('id_4', 'user_id_2', 'category_id_4', 'board_id_2', 0, 0, 0),
|
||||||
|
('id_5', 'user_id_2', 'category_id_5', 'board_id_2', 0, 0, 0),
|
||||||
|
('id_6', 'user_id_3', 'category_id_6', 'board_id_3', 0, 0, 0),
|
||||||
|
('id_7', 'user_id_4', 'category_id_6', 'board_id_4', 0, 0, 0);
|
@ -465,10 +465,15 @@ func (ws *Server) getListenersForBlock(blockID string) []*websocketSession {
|
|||||||
return ws.listenersByBlock[blockID]
|
return ws.listenersByBlock[blockID]
|
||||||
}
|
}
|
||||||
|
|
||||||
// getListenersForTeam returns the listeners subscribed to a
|
// getListenersForUser returns the listener for a user subscribed to a
|
||||||
// team changes.
|
// team changes.
|
||||||
func (ws *Server) getListenersForTeam(teamID string) []*websocketSession {
|
func (ws *Server) getListenerForUser(teamID, userID string) *websocketSession {
|
||||||
return ws.listenersByTeam[teamID]
|
for _, listener := range ws.listenersByTeam[teamID] {
|
||||||
|
if listener.userID == userID {
|
||||||
|
return listener
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getListenersForTeamAndBoard returns the listeners subscribed to a
|
// getListenersForTeamAndBoard returns the listeners subscribed to a
|
||||||
@ -567,16 +572,10 @@ func (ws *Server) BroadcastCategoryChange(category model.Category) {
|
|||||||
Category: &category,
|
Category: &category,
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners := ws.getListenersForTeam(category.TeamID)
|
listener := ws.getListenerForUser(category.TeamID, category.UserID)
|
||||||
ws.logger.Debug("listener(s) for teamID",
|
if listener != nil {
|
||||||
mlog.Int("listener_count", len(listeners)),
|
ws.logger.Debug("Broadcast category change",
|
||||||
mlog.String("teamID", category.TeamID),
|
mlog.String("userID", category.UserID),
|
||||||
mlog.String("categoryID", category.ID),
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, listener := range listeners {
|
|
||||||
ws.logger.Debug("Broadcast block change",
|
|
||||||
mlog.Int("listener_count", len(listeners)),
|
|
||||||
mlog.String("teamID", category.TeamID),
|
mlog.String("teamID", category.TeamID),
|
||||||
mlog.String("categoryID", category.ID),
|
mlog.String("categoryID", category.ID),
|
||||||
mlog.Stringer("remoteAddr", listener.conn.RemoteAddr()),
|
mlog.Stringer("remoteAddr", listener.conn.RemoteAddr()),
|
||||||
@ -596,15 +595,10 @@ func (ws *Server) BroadcastCategoryReorder(teamID, userID string, categoryOrder
|
|||||||
TeamID: teamID,
|
TeamID: teamID,
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners := ws.getListenersForTeam(teamID)
|
listener := ws.getListenerForUser(teamID, userID)
|
||||||
ws.logger.Debug("listener(s) for teamID",
|
if listener != nil {
|
||||||
mlog.Int("listener_count", len(listeners)),
|
|
||||||
mlog.String("teamID", teamID),
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, listener := range listeners {
|
|
||||||
ws.logger.Debug("Broadcast category order change",
|
ws.logger.Debug("Broadcast category order change",
|
||||||
mlog.Int("listener_count", len(listeners)),
|
mlog.String("userID", userID),
|
||||||
mlog.String("teamID", teamID),
|
mlog.String("teamID", teamID),
|
||||||
mlog.Stringer("remoteAddr", listener.conn.RemoteAddr()),
|
mlog.Stringer("remoteAddr", listener.conn.RemoteAddr()),
|
||||||
)
|
)
|
||||||
@ -624,21 +618,17 @@ func (ws *Server) BroadcastCategoryBoardsReorder(teamID, userID, categoryID stri
|
|||||||
TeamID: teamID,
|
TeamID: teamID,
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners := ws.getListenersForTeam(teamID)
|
listener := ws.getListenerForUser(teamID, userID)
|
||||||
ws.logger.Debug("listener(s) for teamID",
|
if listener != nil {
|
||||||
mlog.Int("listener_count", len(listeners)),
|
|
||||||
mlog.String("teamID", teamID),
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, listener := range listeners {
|
|
||||||
ws.logger.Debug("Broadcast board category order change",
|
ws.logger.Debug("Broadcast board category order change",
|
||||||
mlog.Int("listener_count", len(listeners)),
|
mlog.String("userID", userID),
|
||||||
mlog.String("teamID", teamID),
|
mlog.String("teamID", teamID),
|
||||||
|
mlog.String("categoryID", categoryID),
|
||||||
mlog.Stringer("remoteAddr", listener.conn.RemoteAddr()),
|
mlog.Stringer("remoteAddr", listener.conn.RemoteAddr()),
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := listener.WriteJSON(message); err != nil {
|
if err := listener.WriteJSON(message); err != nil {
|
||||||
ws.logger.Error("broadcast category order change error", mlog.Err(err))
|
ws.logger.Error("broadcast category boards order change error", mlog.Err(err))
|
||||||
listener.conn.Close()
|
listener.conn.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -651,16 +641,10 @@ func (ws *Server) BroadcastCategoryBoardChange(teamID, userID string, boardCateg
|
|||||||
BoardCategories: boardCategories,
|
BoardCategories: boardCategories,
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners := ws.getListenersForTeam(teamID)
|
listener := ws.getListenerForUser(teamID, userID)
|
||||||
ws.logger.Debug("listener(s) for teamID",
|
if listener != nil {
|
||||||
mlog.Int("listener_count", len(listeners)),
|
ws.logger.Debug("Broadcast category board change",
|
||||||
mlog.String("teamID", teamID),
|
mlog.String("userID", userID),
|
||||||
mlog.Int("numEntries", len(boardCategories)),
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, listener := range listeners {
|
|
||||||
ws.logger.Debug("Broadcast block change",
|
|
||||||
mlog.Int("listener_count", len(listeners)),
|
|
||||||
mlog.String("teamID", teamID),
|
mlog.String("teamID", teamID),
|
||||||
mlog.Int("numEntries", len(boardCategories)),
|
mlog.Int("numEntries", len(boardCategories)),
|
||||||
mlog.Stringer("remoteAddr", listener.conn.RemoteAddr()),
|
mlog.Stringer("remoteAddr", listener.conn.RemoteAddr()),
|
||||||
|
@ -101,6 +101,48 @@ func TestTeamSubscription(t *testing.T) {
|
|||||||
require.Empty(t, server.listenersByTeam[teamID])
|
require.Empty(t, server.listenersByTeam[teamID])
|
||||||
require.Empty(t, server.listenersByTeam[teamID2])
|
require.Empty(t, server.listenersByTeam[teamID2])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Subscribe users to team retrieve by user", func(t *testing.T) {
|
||||||
|
userID1 := "fake-user-id"
|
||||||
|
userSession1 := &websocketSession{
|
||||||
|
conn: &websocket.Conn{},
|
||||||
|
mu: sync.Mutex{},
|
||||||
|
userID: userID1,
|
||||||
|
teams: []string{},
|
||||||
|
blocks: []string{},
|
||||||
|
}
|
||||||
|
userID2 := "fake-user-id2"
|
||||||
|
userSession2 := &websocketSession{
|
||||||
|
conn: &websocket.Conn{},
|
||||||
|
mu: sync.Mutex{},
|
||||||
|
userID: userID2,
|
||||||
|
teams: []string{},
|
||||||
|
blocks: []string{},
|
||||||
|
}
|
||||||
|
teamID := "fake-team-id"
|
||||||
|
|
||||||
|
server.addListener(session)
|
||||||
|
server.subscribeListenerToTeam(session, teamID)
|
||||||
|
server.addListener(userSession1)
|
||||||
|
server.subscribeListenerToTeam(userSession1, teamID)
|
||||||
|
server.addListener(userSession2)
|
||||||
|
server.subscribeListenerToTeam(userSession2, teamID)
|
||||||
|
|
||||||
|
require.Len(t, server.listeners, 3)
|
||||||
|
require.Len(t, server.listenersByTeam[teamID], 3)
|
||||||
|
|
||||||
|
listener := server.getListenerForUser(teamID, userID1)
|
||||||
|
require.NotNil(t, listener)
|
||||||
|
require.Equal(t, listener.userID, userID1)
|
||||||
|
|
||||||
|
server.removeListener(session)
|
||||||
|
server.removeListener(userSession1)
|
||||||
|
server.removeListener(userSession2)
|
||||||
|
|
||||||
|
require.Empty(t, server.listeners)
|
||||||
|
require.Empty(t, server.listenersByTeam[teamID])
|
||||||
|
require.Empty(t, server.getListenerForUser(teamID, userID1))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBlocksSubscription(t *testing.T) {
|
func TestBlocksSubscription(t *testing.T) {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
{
|
{
|
||||||
"AppBar.Tooltip": "Toggle linked boards",
|
"AdminBadge.SystemAdmin": "Admin",
|
||||||
|
"AdminBadge.TeamAdmin": "Team Admin",
|
||||||
|
"AppBar.Tooltip": "Toggle Linked Boards",
|
||||||
"Attachment.Attachment-title": "Attachment",
|
"Attachment.Attachment-title": "Attachment",
|
||||||
"AttachmentBlock.DeleteAction": "delete",
|
"AttachmentBlock.DeleteAction": "delete",
|
||||||
"AttachmentBlock.addElement": "add {type}",
|
"AttachmentBlock.addElement": "add {type}",
|
||||||
@ -115,7 +117,6 @@
|
|||||||
"CenterPanel.Login": "Login",
|
"CenterPanel.Login": "Login",
|
||||||
"CenterPanel.Share": "Share",
|
"CenterPanel.Share": "Share",
|
||||||
"ChannelIntro.CreateBoard": "Create a board",
|
"ChannelIntro.CreateBoard": "Create a board",
|
||||||
"CloudMessage.cloud-server": "Get your own free cloud server.",
|
|
||||||
"ColorOption.selectColor": "Select {color} Color",
|
"ColorOption.selectColor": "Select {color} Color",
|
||||||
"Comment.delete": "Delete",
|
"Comment.delete": "Delete",
|
||||||
"CommentsList.send": "Send",
|
"CommentsList.send": "Send",
|
||||||
@ -138,6 +139,7 @@
|
|||||||
"ContentBlock.moveDown": "Move down",
|
"ContentBlock.moveDown": "Move down",
|
||||||
"ContentBlock.moveUp": "Move up",
|
"ContentBlock.moveUp": "Move up",
|
||||||
"ContentBlock.text": "text",
|
"ContentBlock.text": "text",
|
||||||
|
"DateFilter.empty": "Empty",
|
||||||
"DateRange.clear": "Clear",
|
"DateRange.clear": "Clear",
|
||||||
"DateRange.empty": "Empty",
|
"DateRange.empty": "Empty",
|
||||||
"DateRange.endDate": "End date",
|
"DateRange.endDate": "End date",
|
||||||
@ -156,10 +158,14 @@
|
|||||||
"Filter.ends-with": "ends with",
|
"Filter.ends-with": "ends with",
|
||||||
"Filter.includes": "includes",
|
"Filter.includes": "includes",
|
||||||
"Filter.is": "is",
|
"Filter.is": "is",
|
||||||
|
"Filter.is-after": "is after",
|
||||||
|
"Filter.is-before": "is before",
|
||||||
"Filter.is-empty": "is empty",
|
"Filter.is-empty": "is empty",
|
||||||
"Filter.is-not-empty": "is not empty",
|
"Filter.is-not-empty": "is not empty",
|
||||||
"Filter.is-not-set": "is not set",
|
"Filter.is-not-set": "is not set",
|
||||||
"Filter.is-set": "is set",
|
"Filter.is-set": "is set",
|
||||||
|
"Filter.isafter": "is after",
|
||||||
|
"Filter.isbefore": "is before",
|
||||||
"Filter.not-contains": "doesn't contain",
|
"Filter.not-contains": "doesn't contain",
|
||||||
"Filter.not-ends-with": "doesn't end with",
|
"Filter.not-ends-with": "doesn't end with",
|
||||||
"Filter.not-includes": "doesn't include",
|
"Filter.not-includes": "doesn't include",
|
||||||
@ -306,6 +312,7 @@
|
|||||||
"ValueSelector.valueSelector": "Value selector",
|
"ValueSelector.valueSelector": "Value selector",
|
||||||
"ValueSelectorLabel.openMenu": "Open menu",
|
"ValueSelectorLabel.openMenu": "Open menu",
|
||||||
"VersionMessage.help": "Check out what's new in this version.",
|
"VersionMessage.help": "Check out what's new in this version.",
|
||||||
|
"VersionMessage.learn-more": "Learn more",
|
||||||
"View.AddView": "Add view",
|
"View.AddView": "Add view",
|
||||||
"View.Board": "Board",
|
"View.Board": "Board",
|
||||||
"View.DeleteView": "Delete view",
|
"View.DeleteView": "Delete view",
|
||||||
@ -360,6 +367,9 @@
|
|||||||
"WelcomePage.StartUsingIt.Text": "Start using it",
|
"WelcomePage.StartUsingIt.Text": "Start using it",
|
||||||
"Workspace.editing-board-template": "You're editing a board template.",
|
"Workspace.editing-board-template": "You're editing a board template.",
|
||||||
"badge.guest": "Guest",
|
"badge.guest": "Guest",
|
||||||
|
"boardPage.confirm-join-button": "Join",
|
||||||
|
"boardPage.confirm-join-text": "You are about to join a private board without explicitly being added by the board admin. Are you sure you wish to join this private board?",
|
||||||
|
"boardPage.confirm-join-title": "Join private board",
|
||||||
"boardSelector.confirm-link-board": "Link board to channel",
|
"boardSelector.confirm-link-board": "Link board to channel",
|
||||||
"boardSelector.confirm-link-board-button": "Yes, link board",
|
"boardSelector.confirm-link-board-button": "Yes, link board",
|
||||||
"boardSelector.confirm-link-board-subtext": "When you link \"{boardName}\" to the channel, all members of the channel (existing and new) will be able to edit it. This excludes members who are guests. You can unlink a board from a channel at any time.",
|
"boardSelector.confirm-link-board-subtext": "When you link \"{boardName}\" to the channel, all members of the channel (existing and new) will be able to edit it. This excludes members who are guests. You can unlink a board from a channel at any time.",
|
||||||
|
@ -2,11 +2,35 @@
|
|||||||
"AppBar.Tooltip": "Alternar quadros vinculados",
|
"AppBar.Tooltip": "Alternar quadros vinculados",
|
||||||
"Attachment.Attachment-title": "Anexo",
|
"Attachment.Attachment-title": "Anexo",
|
||||||
"AttachmentBlock.DeleteAction": "Apagar",
|
"AttachmentBlock.DeleteAction": "Apagar",
|
||||||
"AttachmentBlock.addElement": "Adicionar {tipo}",
|
"AttachmentBlock.addElement": "Adicionar {type}",
|
||||||
"AttachmentBlock.delete": "Anexo apagado.",
|
"AttachmentBlock.delete": "Anexo apagado.",
|
||||||
"AttachmentBlock.failed": "Este arquivo não pôde ser carregado pois ultrapassou o tamanho limite.",
|
"AttachmentBlock.failed": "Este arquivo não pôde ser carregado pois ultrapassou o tamanho limite.",
|
||||||
"AttachmentBlock.upload": "Carregando anexo.",
|
"AttachmentBlock.upload": "Carregando anexo.",
|
||||||
"AttachmentBlock.uploadSuccess": "Anexo carregado.",
|
"AttachmentBlock.uploadSuccess": "Anexo carregado.",
|
||||||
"AttachmentElement.delete-confirmation-dialog-button-text": "Apagar",
|
"AttachmentElement.delete-confirmation-dialog-button-text": "Apagar",
|
||||||
"AttachmentElement.download": "Baixar"
|
"AttachmentElement.download": "Baixar",
|
||||||
|
"BoardComponent.delete": "Apagar",
|
||||||
|
"BoardComponent.hidden-columns": "Colunas escondidas",
|
||||||
|
"BoardComponent.hide": "Esconder",
|
||||||
|
"BoardComponent.new": "+ Novo",
|
||||||
|
"BoardComponent.no-property": "Não {property}",
|
||||||
|
"BoardComponent.show": "Mostrar",
|
||||||
|
"BoardMember.schemeAdmin": "Admin",
|
||||||
|
"BoardMember.schemeCommenter": "Comentador",
|
||||||
|
"BoardMember.schemeEditor": "Editor",
|
||||||
|
"BoardMember.schemeNone": "Nenhum",
|
||||||
|
"BoardPage.newVersion": "Está disponível uma nova versão do Boards, clique aqui para recarregar.",
|
||||||
|
"BoardPage.syncFailed": "O Board pode ter sido apagado ou o acesso revogado.",
|
||||||
|
"BoardTemplateSelector.add-template": "Criar novo modelo",
|
||||||
|
"BoardTemplateSelector.create-empty-board": "Criar um board vazio",
|
||||||
|
"BoardTemplateSelector.delete-template": "Apagar",
|
||||||
|
"BoardTemplateSelector.edit-template": "Editar",
|
||||||
|
"BoardTemplateSelector.plugin.no-content-title": "Criar um board",
|
||||||
|
"BoardTemplateSelector.title": "Criar um board",
|
||||||
|
"BoardTemplateSelector.use-this-template": "Usar este modelo",
|
||||||
|
"BoardsSwitcher.Title": "Encontrar boards",
|
||||||
|
"Calculations.Options.average.label": "Média",
|
||||||
|
"shareBoard.members-select-group": "Membros",
|
||||||
|
"shareBoard.unknown-channel-display-name": "Canal desconhecido",
|
||||||
|
"tutorial_tip.ok": "Próximo"
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@
|
|||||||
"CardDetailProperty.property-name-change-subtext": "digite de \"{oldPropType}\" para \"{newPropType}\"",
|
"CardDetailProperty.property-name-change-subtext": "digite de \"{oldPropType}\" para \"{newPropType}\"",
|
||||||
"CardDetial.limited-link": "Saiba mais sobre nossos planos.",
|
"CardDetial.limited-link": "Saiba mais sobre nossos planos.",
|
||||||
"CardDialog.delete-confirmation-dialog-button-text": "Excluir",
|
"CardDialog.delete-confirmation-dialog-button-text": "Excluir",
|
||||||
"CardDialog.delete-confirmation-dialog-heading": "Confirmar exclusão do card!",
|
"CardDialog.delete-confirmation-dialog-heading": "Confirmar exclusão do cartão",
|
||||||
"CardDialog.editing-template": "Você está editando um template.",
|
"CardDialog.editing-template": "Você está editando um template.",
|
||||||
"CardDialog.nocard": "Esse card não existe ou não está acessível.",
|
"CardDialog.nocard": "Esse card não existe ou não está acessível.",
|
||||||
"Categories.CreateCategoryDialog.CancelText": "Cancelar",
|
"Categories.CreateCategoryDialog.CancelText": "Cancelar",
|
||||||
@ -163,6 +163,7 @@
|
|||||||
"FilterByText.placeholder": "filtrar texto",
|
"FilterByText.placeholder": "filtrar texto",
|
||||||
"FilterComponent.add-filter": "+ Adicionar filtro",
|
"FilterComponent.add-filter": "+ Adicionar filtro",
|
||||||
"FilterComponent.delete": "Excluir",
|
"FilterComponent.delete": "Excluir",
|
||||||
|
"FilterValue.empty": "(vazio)",
|
||||||
"FindBoardsDialog.IntroText": "Procurar por quadros",
|
"FindBoardsDialog.IntroText": "Procurar por quadros",
|
||||||
"FindBoardsDialog.NoResultsFor": "Sem resultado para \"{searchQuery}\"",
|
"FindBoardsDialog.NoResultsFor": "Sem resultado para \"{searchQuery}\"",
|
||||||
"FindBoardsDialog.NoResultsSubtext": "Verifique a digitação ou tente outra busca.",
|
"FindBoardsDialog.NoResultsSubtext": "Verifique a digitação ou tente outra busca.",
|
||||||
@ -271,6 +272,7 @@
|
|||||||
"SidebarTour.SidebarCategories.Link": "Saiba mais",
|
"SidebarTour.SidebarCategories.Link": "Saiba mais",
|
||||||
"SidebarTour.SidebarCategories.Title": "Categorias de barra lateral",
|
"SidebarTour.SidebarCategories.Title": "Categorias de barra lateral",
|
||||||
"SiteStats.total_boards": "Total de boards",
|
"SiteStats.total_boards": "Total de boards",
|
||||||
|
"SiteStats.total_cards": "Total de cartões",
|
||||||
"TableComponent.add-icon": "Adicionar Ícone",
|
"TableComponent.add-icon": "Adicionar Ícone",
|
||||||
"TableComponent.name": "Nome",
|
"TableComponent.name": "Nome",
|
||||||
"TableComponent.plus-new": "+ Novo",
|
"TableComponent.plus-new": "+ Novo",
|
||||||
@ -368,7 +370,7 @@
|
|||||||
"createImageBlock.failed": "Não foi possível enviar o arquivo. Limite de tamanho alcançado.",
|
"createImageBlock.failed": "Não foi possível enviar o arquivo. Limite de tamanho alcançado.",
|
||||||
"default-properties.badges": "Comentários e descrição",
|
"default-properties.badges": "Comentários e descrição",
|
||||||
"default-properties.title": "Título",
|
"default-properties.title": "Título",
|
||||||
"error.back-to-home": "Volta para Home",
|
"error.back-to-home": "Volta para o início",
|
||||||
"error.back-to-team": "Volta para o time",
|
"error.back-to-team": "Volta para o time",
|
||||||
"error.board-not-found": "Quadro não encontrado.",
|
"error.board-not-found": "Quadro não encontrado.",
|
||||||
"error.go-login": "Log in",
|
"error.go-login": "Log in",
|
||||||
@ -385,7 +387,10 @@
|
|||||||
"login.log-in-button": "Entrar",
|
"login.log-in-button": "Entrar",
|
||||||
"login.log-in-title": "Entrar",
|
"login.log-in-title": "Entrar",
|
||||||
"login.register-button": "ou criar uma conta se você ainda não tiver uma",
|
"login.register-button": "ou criar uma conta se você ainda não tiver uma",
|
||||||
|
"new_channel_modal.create_board.empty_board_description": "Criar um novo quadro vazio",
|
||||||
|
"new_channel_modal.create_board.empty_board_title": "Quadro vazio",
|
||||||
"new_channel_modal.create_board.select_template_placeholder": "Selecionar um modelo",
|
"new_channel_modal.create_board.select_template_placeholder": "Selecionar um modelo",
|
||||||
|
"new_channel_modal.create_board.title": "Criar um quadro para este canal",
|
||||||
"notification-box-card-limit-reached.close-tooltip": "Soneca por 10 dias",
|
"notification-box-card-limit-reached.close-tooltip": "Soneca por 10 dias",
|
||||||
"notification-box-card-limit-reached.contact-link": "notificar seu admin",
|
"notification-box-card-limit-reached.contact-link": "notificar seu admin",
|
||||||
"notification-box-card-limit-reached.link": "Atualizar para um plano pago",
|
"notification-box-card-limit-reached.link": "Atualizar para um plano pago",
|
||||||
@ -400,11 +405,11 @@
|
|||||||
"person.add-user-to-board-warning": "{username} não é um membro de um board, e não será notificado.",
|
"person.add-user-to-board-warning": "{username} não é um membro de um board, e não será notificado.",
|
||||||
"register.login-button": "ou entre se você já tem uma conta",
|
"register.login-button": "ou entre se você já tem uma conta",
|
||||||
"register.signup-title": "Registrar uma conta",
|
"register.signup-title": "Registrar uma conta",
|
||||||
"rhs-board-non-admin-msg": "Você não é admin de um board",
|
"rhs-board-non-admin-msg": "Você não é um adminstrador do quadro",
|
||||||
"rhs-boards.add": "Adicionar",
|
"rhs-boards.add": "Adicionar",
|
||||||
"rhs-boards.dm": "DM",
|
"rhs-boards.dm": "DM",
|
||||||
"rhs-boards.gm": "GM",
|
"rhs-boards.gm": "GM",
|
||||||
"rhs-boards.header.dm": "esta Direct Message",
|
"rhs-boards.header.dm": "esta mensagem direta",
|
||||||
"rhs-boards.header.gm": "esta mensagem de grupo",
|
"rhs-boards.header.gm": "esta mensagem de grupo",
|
||||||
"rhs-boards.last-update-at": "Última atualização em: {datetime}",
|
"rhs-boards.last-update-at": "Última atualização em: {datetime}",
|
||||||
"rhs-boards.link-boards-to-channel": "Vincular boards para {channelName}",
|
"rhs-boards.link-boards-to-channel": "Vincular boards para {channelName}",
|
||||||
|
@ -221,7 +221,7 @@
|
|||||||
"PropertyType.UpdatedTime": "Son güncelleme zamanı",
|
"PropertyType.UpdatedTime": "Son güncelleme zamanı",
|
||||||
"PropertyType.Url": "Adres",
|
"PropertyType.Url": "Adres",
|
||||||
"PropertyValueElement.empty": "Boş",
|
"PropertyValueElement.empty": "Boş",
|
||||||
"RegistrationLink.confirmRegenerateToken": "Bu işlem daha önce paylaşılmış bağlantıları geçersiz kılacak. Devam etmek istiyor musunuz?",
|
"RegistrationLink.confirmRegenerateToken": "Bu işlem daha önce paylaşılmış bağlantıları geçersiz kılacak. İlerlemek istiyor musunuz?",
|
||||||
"RegistrationLink.copiedLink": "Kopyalandı!",
|
"RegistrationLink.copiedLink": "Kopyalandı!",
|
||||||
"RegistrationLink.copyLink": "Bağlantıyı kopyala",
|
"RegistrationLink.copyLink": "Bağlantıyı kopyala",
|
||||||
"RegistrationLink.description": "Başkalarının hesap ekleyebilmesi için bu bağlantıyı paylaş:",
|
"RegistrationLink.description": "Başkalarının hesap ekleyebilmesi için bu bağlantıyı paylaş:",
|
||||||
@ -232,7 +232,7 @@
|
|||||||
"ShareBoard.ShareInternal": "İçeride paylaş",
|
"ShareBoard.ShareInternal": "İçeride paylaş",
|
||||||
"ShareBoard.ShareInternalDescription": "İzni olan kullanıcılar bu bağlantıyı kullanabilecek.",
|
"ShareBoard.ShareInternalDescription": "İzni olan kullanıcılar bu bağlantıyı kullanabilecek.",
|
||||||
"ShareBoard.Title": "Panoyu paylaş",
|
"ShareBoard.Title": "Panoyu paylaş",
|
||||||
"ShareBoard.confirmRegenerateToken": "Bu işlem daha önce paylaşılmış bağlantıları geçersiz kılacak. Devam etmek istiyor musunuz?",
|
"ShareBoard.confirmRegenerateToken": "Bu işlem daha önce paylaşılmış bağlantıları geçersiz kılacak. İlerlemek istiyor musunuz?",
|
||||||
"ShareBoard.copiedLink": "Kopyalandı!",
|
"ShareBoard.copiedLink": "Kopyalandı!",
|
||||||
"ShareBoard.copyLink": "Bağlantıyı kopyala",
|
"ShareBoard.copyLink": "Bağlantıyı kopyala",
|
||||||
"ShareBoard.regenerate": "Kodu yeniden oluştur",
|
"ShareBoard.regenerate": "Kodu yeniden oluştur",
|
||||||
@ -395,6 +395,10 @@
|
|||||||
"login.log-in-button": "Oturum aç",
|
"login.log-in-button": "Oturum aç",
|
||||||
"login.log-in-title": "Oturum açın",
|
"login.log-in-title": "Oturum açın",
|
||||||
"login.register-button": "ya da hesabınız yoksa bir hesap açın",
|
"login.register-button": "ya da hesabınız yoksa bir hesap açın",
|
||||||
|
"new_channel_modal.create_board.empty_board_description": "Yeni boş bir pano oluştur",
|
||||||
|
"new_channel_modal.create_board.empty_board_title": "Boş pano",
|
||||||
|
"new_channel_modal.create_board.select_template_placeholder": "Bir kalıp seçin",
|
||||||
|
"new_channel_modal.create_board.title": "Bu kanal için bir pano oluştur",
|
||||||
"notification-box-card-limit-reached.close-tooltip": "10 gün için sustur",
|
"notification-box-card-limit-reached.close-tooltip": "10 gün için sustur",
|
||||||
"notification-box-card-limit-reached.contact-link": "yöneticinizi bilgilendirin",
|
"notification-box-card-limit-reached.contact-link": "yöneticinizi bilgilendirin",
|
||||||
"notification-box-card-limit-reached.link": "Ücretli bir tarifeye geçin",
|
"notification-box-card-limit-reached.link": "Ücretli bir tarifeye geçin",
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`components/messages/CloudMessage not plugin mode, close message 1`] = `<div />`;
|
|
||||||
|
|
||||||
exports[`components/messages/CloudMessage not plugin mode, show message, close message 1`] = `
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
class="CloudMessage"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="banner"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
class="CompassIcon icon-information-outline CompassIcon"
|
|
||||||
/>
|
|
||||||
Get your own free cloud server.
|
|
||||||
<button
|
|
||||||
title="Learn more"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
Learn more
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
aria-label="Close dialog"
|
|
||||||
title="Close dialog"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
class="CompassIcon icon-close CloseIcon"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/messages/CloudMessage not plugin mode, single user, close message 1`] = `
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
class="CloudMessage"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="banner"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
class="CompassIcon icon-information-outline CompassIcon"
|
|
||||||
/>
|
|
||||||
Get your own free cloud server.
|
|
||||||
<button
|
|
||||||
title="Learn more"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
Learn more
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
aria-label="Close dialog"
|
|
||||||
title="Close dialog"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
class="CompassIcon icon-close CloseIcon"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`components/messages/CloudMessage plugin mode, no display 1`] = `<div />`;
|
|
@ -1,38 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
.CloudMessage {
|
|
||||||
background-color: rgb(var(--sidebar-text-active-border-rgb));
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
|
||||||
font-weight: 600;
|
|
||||||
|
|
||||||
div {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .banner {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 10px;
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
.CompassIcon {
|
|
||||||
font-size: 18px;
|
|
||||||
margin-right: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Button {
|
|
||||||
margin-left: 8px;
|
|
||||||
background-color: rgba(255, 255, 255, 0.16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.IconButton {
|
|
||||||
float: right;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,190 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import React from 'react'
|
|
||||||
import {Provider as ReduxProvider} from 'react-redux'
|
|
||||||
|
|
||||||
import {render, screen} from '@testing-library/react'
|
|
||||||
import {mocked} from 'jest-mock'
|
|
||||||
import userEvent from '@testing-library/user-event'
|
|
||||||
|
|
||||||
import configureStore from 'redux-mock-store'
|
|
||||||
|
|
||||||
import {Utils} from '../../utils'
|
|
||||||
|
|
||||||
import {IUser} from '../../user'
|
|
||||||
|
|
||||||
import {wrapIntl} from '../../testUtils'
|
|
||||||
|
|
||||||
import client from '../../octoClient'
|
|
||||||
|
|
||||||
import {UserSettings} from '../../userSettings'
|
|
||||||
|
|
||||||
import CloudMessage from './cloudMessage'
|
|
||||||
|
|
||||||
jest.mock('../../utils')
|
|
||||||
jest.mock('../../octoClient')
|
|
||||||
const mockedOctoClient = mocked(client, true)
|
|
||||||
|
|
||||||
describe('components/messages/CloudMessage', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
})
|
|
||||||
|
|
||||||
const mockedUtils = mocked(Utils, true)
|
|
||||||
const mockStore = configureStore([])
|
|
||||||
|
|
||||||
test('plugin mode, no display', () => {
|
|
||||||
mockedUtils.isFocalboardPlugin.mockReturnValue(true)
|
|
||||||
|
|
||||||
const me: IUser = {
|
|
||||||
id: 'user-id-1',
|
|
||||||
username: 'username_1',
|
|
||||||
email: '',
|
|
||||||
nickname: '',
|
|
||||||
firstname: '',
|
|
||||||
lastname: '',
|
|
||||||
props: {},
|
|
||||||
create_at: 0,
|
|
||||||
update_at: 0,
|
|
||||||
is_bot: false,
|
|
||||||
is_guest: false,
|
|
||||||
roles: 'system_user',
|
|
||||||
}
|
|
||||||
const state = {
|
|
||||||
users: {
|
|
||||||
me,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const store = mockStore(state)
|
|
||||||
|
|
||||||
const component = wrapIntl(
|
|
||||||
<ReduxProvider store={store}>
|
|
||||||
<CloudMessage/>
|
|
||||||
</ReduxProvider>,
|
|
||||||
)
|
|
||||||
|
|
||||||
const {container} = render(component)
|
|
||||||
expect(container).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('not plugin mode, close message', () => {
|
|
||||||
const me: IUser = {
|
|
||||||
id: 'user-id-1',
|
|
||||||
username: 'username_1',
|
|
||||||
email: '',
|
|
||||||
nickname: '',
|
|
||||||
firstname: '',
|
|
||||||
lastname: '',
|
|
||||||
create_at: 0,
|
|
||||||
update_at: 0,
|
|
||||||
is_bot: false,
|
|
||||||
is_guest: false,
|
|
||||||
roles: 'system_user',
|
|
||||||
props: {},
|
|
||||||
}
|
|
||||||
const state = {
|
|
||||||
users: {
|
|
||||||
me,
|
|
||||||
myConfig: {
|
|
||||||
cloudMessageCanceled: {value: 'true'},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const store = mockStore(state)
|
|
||||||
mockedUtils.isFocalboardPlugin.mockReturnValue(false)
|
|
||||||
|
|
||||||
const component = wrapIntl(
|
|
||||||
<ReduxProvider store={store}>
|
|
||||||
<CloudMessage/>
|
|
||||||
</ReduxProvider>,
|
|
||||||
)
|
|
||||||
|
|
||||||
const {container} = render(component)
|
|
||||||
expect(container).toMatchSnapshot()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('not plugin mode, show message, close message', () => {
|
|
||||||
const me: IUser = {
|
|
||||||
id: 'user-id-1',
|
|
||||||
username: 'username_1',
|
|
||||||
email: '',
|
|
||||||
nickname: '',
|
|
||||||
firstname: '',
|
|
||||||
lastname: '',
|
|
||||||
props: {},
|
|
||||||
create_at: 0,
|
|
||||||
update_at: 0,
|
|
||||||
is_bot: false,
|
|
||||||
is_guest: false,
|
|
||||||
roles: 'system_user',
|
|
||||||
}
|
|
||||||
const state = {
|
|
||||||
users: {
|
|
||||||
me,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const store = mockStore(state)
|
|
||||||
mockedUtils.isFocalboardPlugin.mockReturnValue(false)
|
|
||||||
|
|
||||||
const component = wrapIntl(
|
|
||||||
<ReduxProvider store={store}>
|
|
||||||
<CloudMessage/>
|
|
||||||
</ReduxProvider>,
|
|
||||||
)
|
|
||||||
|
|
||||||
const {container} = render(component)
|
|
||||||
expect(container).toMatchSnapshot()
|
|
||||||
|
|
||||||
const buttonElement = screen.getByRole('button', {name: 'Close dialog'})
|
|
||||||
userEvent.click(buttonElement)
|
|
||||||
expect(mockedOctoClient.patchUserConfig).toBeCalledWith('user-id-1', {
|
|
||||||
updatedFields: {
|
|
||||||
cloudMessageCanceled: 'true',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test('not plugin mode, single user, close message', () => {
|
|
||||||
const me: IUser = {
|
|
||||||
id: 'single-user',
|
|
||||||
username: 'single-user',
|
|
||||||
email: 'single-user',
|
|
||||||
nickname: '',
|
|
||||||
firstname: '',
|
|
||||||
lastname: '',
|
|
||||||
props: {},
|
|
||||||
create_at: 0,
|
|
||||||
update_at: Date.now() - (1000 * 60 * 60 * 24), //24 hours,
|
|
||||||
is_bot: false,
|
|
||||||
is_guest: false,
|
|
||||||
roles: 'system_user',
|
|
||||||
}
|
|
||||||
const state = {
|
|
||||||
users: {
|
|
||||||
me,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const store = mockStore(state)
|
|
||||||
const hideCloudMessageSpy = jest.spyOn(UserSettings, 'hideCloudMessage', 'set')
|
|
||||||
|
|
||||||
mockedUtils.isFocalboardPlugin.mockReturnValue(false)
|
|
||||||
|
|
||||||
const component = wrapIntl(
|
|
||||||
<ReduxProvider store={store}>
|
|
||||||
<CloudMessage/>
|
|
||||||
</ReduxProvider>,
|
|
||||||
)
|
|
||||||
|
|
||||||
const {container} = render(component)
|
|
||||||
expect(container).toMatchSnapshot()
|
|
||||||
|
|
||||||
const buttonElement = screen.getByRole('button', {name: 'Close dialog'})
|
|
||||||
userEvent.click(buttonElement)
|
|
||||||
|
|
||||||
expect(mockedOctoClient.patchUserConfig).toBeCalledTimes(0)
|
|
||||||
expect(hideCloudMessageSpy).toHaveBeenCalledWith(true)
|
|
||||||
expect(UserSettings.hideCloudMessage).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,114 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
import {useIntl, FormattedMessage} from 'react-intl'
|
|
||||||
|
|
||||||
import {Utils} from '../../utils'
|
|
||||||
import IconButton from '../../widgets/buttons/iconButton'
|
|
||||||
import Button from '../../widgets/buttons/button'
|
|
||||||
|
|
||||||
import CloseIcon from '../../widgets/icons/close'
|
|
||||||
|
|
||||||
import {useAppSelector, useAppDispatch} from '../../store/hooks'
|
|
||||||
import octoClient from '../../octoClient'
|
|
||||||
import {IUser, UserConfigPatch} from '../../user'
|
|
||||||
import {getMe, patchProps, getCloudMessageCanceled} from '../../store/users'
|
|
||||||
import {UserSettings} from '../../userSettings'
|
|
||||||
|
|
||||||
import CompassIcon from '../../widgets/icons/compassIcon'
|
|
||||||
import TelemetryClient, {TelemetryCategory, TelemetryActions} from '../../telemetry/telemetryClient'
|
|
||||||
|
|
||||||
import './cloudMessage.scss'
|
|
||||||
const signupURL = 'https://mattermost.com/pricing'
|
|
||||||
const displayAfter = (1000 * 60 * 60 * 24) //24 hours
|
|
||||||
|
|
||||||
const CloudMessage = React.memo(() => {
|
|
||||||
const intl = useIntl()
|
|
||||||
const dispatch = useAppDispatch()
|
|
||||||
const me = useAppSelector<IUser|null>(getMe)
|
|
||||||
const cloudMessageCanceled = useAppSelector(getCloudMessageCanceled)
|
|
||||||
|
|
||||||
const closeDialogText = intl.formatMessage({
|
|
||||||
id: 'Dialog.closeDialog',
|
|
||||||
defaultMessage: 'Close dialog',
|
|
||||||
})
|
|
||||||
|
|
||||||
const onClose = async () => {
|
|
||||||
if (me) {
|
|
||||||
if (me.id === 'single-user') {
|
|
||||||
UserSettings.hideCloudMessage = true
|
|
||||||
dispatch(patchProps([
|
|
||||||
{
|
|
||||||
user_id: me.id,
|
|
||||||
category: 'focalboard',
|
|
||||||
name: 'cloudMessageCanceled',
|
|
||||||
value: 'true',
|
|
||||||
},
|
|
||||||
]))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const patch: UserConfigPatch = {
|
|
||||||
updatedFields: {
|
|
||||||
cloudMessageCanceled: 'true',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const patchedProps = await octoClient.patchUserConfig(me.id, patch)
|
|
||||||
if (patchedProps) {
|
|
||||||
dispatch(patchProps(patchedProps))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Utils.isFocalboardPlugin() || cloudMessageCanceled) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (me) {
|
|
||||||
const installTime = Date.now() - me.create_at
|
|
||||||
if (installTime < displayAfter) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='CloudMessage'>
|
|
||||||
<div className='banner'>
|
|
||||||
<CompassIcon
|
|
||||||
icon='information-outline'
|
|
||||||
className='CompassIcon'
|
|
||||||
/>
|
|
||||||
<FormattedMessage
|
|
||||||
id='CloudMessage.cloud-server'
|
|
||||||
defaultMessage='Get your own free cloud server.'
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
title='Learn more'
|
|
||||||
size='xsmall'
|
|
||||||
emphasis='primary'
|
|
||||||
onClick={() => {
|
|
||||||
TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.CloudMoreInfo)
|
|
||||||
window.open(signupURL)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<FormattedMessage
|
|
||||||
id='cloudMessage.learn-more'
|
|
||||||
defaultMessage='Learn more'
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<IconButton
|
|
||||||
className='margin-right'
|
|
||||||
onClick={onClose}
|
|
||||||
icon={<CloseIcon/>}
|
|
||||||
title={closeDialogText}
|
|
||||||
size='small'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
export default CloudMessage
|
|
@ -71,7 +71,7 @@ const VersionMessage = React.memo(() => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='cloudMessage.learn-more'
|
id='VersionMessage.learn-more'
|
||||||
defaultMessage='Learn more'
|
defaultMessage='Learn more'
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dialog {
|
||||||
|
color: rgba(var(--center-channel-color-rgb));
|
||||||
|
}
|
||||||
|
|
||||||
.octo-sidebar-item {
|
.octo-sidebar-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
.BoardPage {
|
.BoardPage {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.CloudMessage:not(:first-child),
|
|
||||||
.VersionMessage:not(:first-child) {
|
.VersionMessage:not(:first-child) {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -6,7 +6,6 @@ import {FormattedMessage, useIntl} from 'react-intl'
|
|||||||
import {useRouteMatch, useHistory} from 'react-router-dom'
|
import {useRouteMatch, useHistory} from 'react-router-dom'
|
||||||
|
|
||||||
import Workspace from '../../components/workspace'
|
import Workspace from '../../components/workspace'
|
||||||
import CloudMessage from '../../components/messages/cloudMessage'
|
|
||||||
import VersionMessage from '../../components/messages/versionMessage'
|
import VersionMessage from '../../components/messages/versionMessage'
|
||||||
import octoClient from '../../octoClient'
|
import octoClient from '../../octoClient'
|
||||||
import {Subscription, WSClient} from '../../wsclient'
|
import {Subscription, WSClient} from '../../wsclient'
|
||||||
@ -300,7 +299,6 @@ const BoardPage = (props: Props): JSX.Element => {
|
|||||||
<SetWindowTitleAndIcon/>
|
<SetWindowTitleAndIcon/>
|
||||||
<UndoRedoHotKeys/>
|
<UndoRedoHotKeys/>
|
||||||
<WebsocketConnection/>
|
<WebsocketConnection/>
|
||||||
<CloudMessage/>
|
|
||||||
<VersionMessage/>
|
<VersionMessage/>
|
||||||
|
|
||||||
{!mobileWarningClosed &&
|
{!mobileWarningClosed &&
|
||||||
|
@ -10,10 +10,6 @@ import {Utils} from '../utils'
|
|||||||
|
|
||||||
import {Subscription} from '../wsclient'
|
import {Subscription} from '../wsclient'
|
||||||
|
|
||||||
// TODO: change this whene the initial load is complete
|
|
||||||
// import {initialLoad} from './initialLoad'
|
|
||||||
import {UserSettings} from '../userSettings'
|
|
||||||
|
|
||||||
import {initialLoad} from './initialLoad'
|
import {initialLoad} from './initialLoad'
|
||||||
|
|
||||||
import {RootState} from './index'
|
import {RootState} from './index'
|
||||||
@ -169,20 +165,6 @@ export const getOnboardingTourCategory = createSelector(
|
|||||||
(myConfig): string => (myConfig.tourCategory ? myConfig.tourCategory.value : ''),
|
(myConfig): string => (myConfig.tourCategory ? myConfig.tourCategory.value : ''),
|
||||||
)
|
)
|
||||||
|
|
||||||
export const getCloudMessageCanceled = createSelector(
|
|
||||||
getMe,
|
|
||||||
getMyConfig,
|
|
||||||
(me, myConfig): boolean => {
|
|
||||||
if (!me) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (me.id === 'single-user') {
|
|
||||||
return UserSettings.hideCloudMessage
|
|
||||||
}
|
|
||||||
return Boolean(myConfig.cloudMessageCanceled?.value)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
export const getVersionMessageCanceled = createSelector(
|
export const getVersionMessageCanceled = createSelector(
|
||||||
getMe,
|
getMe,
|
||||||
getMyConfig,
|
getMyConfig,
|
||||||
|
@ -17,7 +17,6 @@ export enum UserSettingKey {
|
|||||||
RandomIcons = 'randomIcons',
|
RandomIcons = 'randomIcons',
|
||||||
MobileWarningClosed = 'mobileWarningClosed',
|
MobileWarningClosed = 'mobileWarningClosed',
|
||||||
WelcomePageViewed = 'welcomePageViewed',
|
WelcomePageViewed = 'welcomePageViewed',
|
||||||
HideCloudMessage = 'hideCloudMessage',
|
|
||||||
NameFormat = 'nameFormat'
|
NameFormat = 'nameFormat'
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,14 +148,6 @@ export class UserSettings {
|
|||||||
UserSettings.set(UserSettingKey.MobileWarningClosed, String(newValue))
|
UserSettings.set(UserSettingKey.MobileWarningClosed, String(newValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
static get hideCloudMessage(): boolean {
|
|
||||||
return localStorage.getItem(UserSettingKey.HideCloudMessage) === 'true'
|
|
||||||
}
|
|
||||||
|
|
||||||
static set hideCloudMessage(newValue: boolean) {
|
|
||||||
localStorage.setItem(UserSettingKey.HideCloudMessage, JSON.stringify(newValue))
|
|
||||||
}
|
|
||||||
|
|
||||||
static get nameFormat(): string | null {
|
static get nameFormat(): string | null {
|
||||||
return UserSettings.get(UserSettingKey.NameFormat)
|
return UserSettings.get(UserSettingKey.NameFormat)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user