Merge branch 'master' into mm-1355
Этот коммит содержится в:
2
Godeps/Godeps.json
сгенерированный
2
Godeps/Godeps.json
сгенерированный
@@ -80,7 +80,7 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/nfnt/resize",
|
||||
"Rev": "f2d1b73023c55bdfb4fa2ed385d2bec95bce3692"
|
||||
"Rev": "dc93e1b98c579d90ee2fa15c1fd6dac34f6e7899"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/stretchr/objx",
|
||||
|
||||
6
Godeps/_workspace/src/github.com/nfnt/resize/converter.go
сгенерированный
поставляемый
6
Godeps/_workspace/src/github.com/nfnt/resize/converter.go
сгенерированный
поставляемый
@@ -131,11 +131,11 @@ func resizeRGBA(in *image.RGBA, out *image.NRGBA, scale float64, coeffs []int16,
|
||||
|
||||
// reverse alpha-premultiplication.
|
||||
if a != 0 {
|
||||
r *= 0xffff
|
||||
r *= 0xff
|
||||
r /= a
|
||||
g *= 0xffff
|
||||
g *= 0xff
|
||||
g /= a
|
||||
b *= 0xffff
|
||||
b *= 0xff
|
||||
b /= a
|
||||
}
|
||||
|
||||
|
||||
4
Godeps/_workspace/src/github.com/nfnt/resize/resize.go
сгенерированный
поставляемый
4
Godeps/_workspace/src/github.com/nfnt/resize/resize.go
сгенерированный
поставляемый
@@ -167,7 +167,7 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i
|
||||
// accessing the YCbCr arrays in a tight loop is slow.
|
||||
// converting the image to ycc increases performance by 2x.
|
||||
temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio)
|
||||
result := newYCC(image.Rect(0, 0, int(width), int(height)), input.SubsampleRatio)
|
||||
result := newYCC(image.Rect(0, 0, int(width), int(height)), image.YCbCrSubsampleRatio444)
|
||||
|
||||
coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel)
|
||||
in := imageYCbCrToYCC(input)
|
||||
@@ -409,7 +409,7 @@ func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image,
|
||||
// accessing the YCbCr arrays in a tight loop is slow.
|
||||
// converting the image to ycc increases performance by 2x.
|
||||
temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio)
|
||||
result := newYCC(image.Rect(0, 0, int(width), int(height)), input.SubsampleRatio)
|
||||
result := newYCC(image.Rect(0, 0, int(width), int(height)), image.YCbCrSubsampleRatio444)
|
||||
|
||||
coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX)
|
||||
in := imageYCbCrToYCC(input)
|
||||
|
||||
96
Godeps/_workspace/src/github.com/nfnt/resize/resize_test.go
сгенерированный
поставляемый
96
Godeps/_workspace/src/github.com/nfnt/resize/resize_test.go
сгенерированный
поставляемый
@@ -46,7 +46,7 @@ func Test_CorrectResize(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SameColor(t *testing.T) {
|
||||
func Test_SameColorWithRGBA(t *testing.T) {
|
||||
img := image.NewRGBA(image.Rect(0, 0, 20, 20))
|
||||
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
|
||||
for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
|
||||
@@ -56,9 +56,99 @@ func Test_SameColor(t *testing.T) {
|
||||
out := Resize(10, 10, img, Lanczos3)
|
||||
for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
|
||||
for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
|
||||
color := img.At(x, y).(color.RGBA)
|
||||
color := out.At(x, y).(color.NRGBA)
|
||||
if color.R != 0x80 || color.G != 0x80 || color.B != 0x80 || color.A != 0xFF {
|
||||
t.Fail()
|
||||
t.Errorf("%+v", color)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SameColorWithNRGBA(t *testing.T) {
|
||||
img := image.NewNRGBA(image.Rect(0, 0, 20, 20))
|
||||
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
|
||||
for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
|
||||
img.SetNRGBA(x, y, color.NRGBA{0x80, 0x80, 0x80, 0xFF})
|
||||
}
|
||||
}
|
||||
out := Resize(10, 10, img, Lanczos3)
|
||||
for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
|
||||
for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
|
||||
color := out.At(x, y).(color.NRGBA)
|
||||
if color.R != 0x80 || color.G != 0x80 || color.B != 0x80 || color.A != 0xFF {
|
||||
t.Errorf("%+v", color)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SameColorWithRGBA64(t *testing.T) {
|
||||
img := image.NewRGBA64(image.Rect(0, 0, 20, 20))
|
||||
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
|
||||
for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
|
||||
img.SetRGBA64(x, y, color.RGBA64{0x8000, 0x8000, 0x8000, 0xFFFF})
|
||||
}
|
||||
}
|
||||
out := Resize(10, 10, img, Lanczos3)
|
||||
for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
|
||||
for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
|
||||
color := out.At(x, y).(color.NRGBA64)
|
||||
if color.R != 0x8000 || color.G != 0x8000 || color.B != 0x8000 || color.A != 0xFFFF {
|
||||
t.Errorf("%+v", color)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SameColorWithNRGBA64(t *testing.T) {
|
||||
img := image.NewNRGBA64(image.Rect(0, 0, 20, 20))
|
||||
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
|
||||
for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
|
||||
img.SetNRGBA64(x, y, color.NRGBA64{0x8000, 0x8000, 0x8000, 0xFFFF})
|
||||
}
|
||||
}
|
||||
out := Resize(10, 10, img, Lanczos3)
|
||||
for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
|
||||
for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
|
||||
color := out.At(x, y).(color.NRGBA64)
|
||||
if color.R != 0x8000 || color.G != 0x8000 || color.B != 0x8000 || color.A != 0xFFFF {
|
||||
t.Errorf("%+v", color)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SameColorWithGray(t *testing.T) {
|
||||
img := image.NewGray(image.Rect(0, 0, 20, 20))
|
||||
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
|
||||
for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
|
||||
img.SetGray(x, y, color.Gray{0x80})
|
||||
}
|
||||
}
|
||||
out := Resize(10, 10, img, Lanczos3)
|
||||
for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
|
||||
for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
|
||||
color := out.At(x, y).(color.Gray)
|
||||
if color.Y != 0x80 {
|
||||
t.Errorf("%+v", color)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SameColorWithGray16(t *testing.T) {
|
||||
img := image.NewGray16(image.Rect(0, 0, 20, 20))
|
||||
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
|
||||
for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
|
||||
img.SetGray16(x, y, color.Gray16{0x8000})
|
||||
}
|
||||
}
|
||||
out := Resize(10, 10, img, Lanczos3)
|
||||
for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ {
|
||||
for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ {
|
||||
color := out.At(x, y).(color.Gray16)
|
||||
if color.Y != 0x8000 {
|
||||
t.Errorf("%+v", color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
54
Makefile
54
Makefile
@@ -4,6 +4,8 @@ GOPATH ?= $(GOPATH:)
|
||||
GOFLAGS ?= $(GOFLAGS:)
|
||||
BUILD_NUMBER ?= $(BUILD_NUMBER:)
|
||||
|
||||
GO=$(GOPATH)/bin/godep go
|
||||
|
||||
ifeq ($(BUILD_NUMBER),)
|
||||
BUILD_NUMBER := dev
|
||||
endif
|
||||
@@ -21,29 +23,27 @@ travis:
|
||||
@echo building for travis
|
||||
|
||||
rm -Rf $(DIST_ROOT)
|
||||
@go clean $(GOFLAGS) -i ./...
|
||||
|
||||
@$(GO) clean $(GOFLAGS) -i ./...
|
||||
|
||||
@cd web/react/ && npm install
|
||||
|
||||
@go build $(GOFLAGS) ./...
|
||||
@$(GO) build $(GOFLAGS) ./...
|
||||
|
||||
@mkdir -p logs
|
||||
|
||||
@go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=180s ./api || exit 1
|
||||
@go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=12s ./model || exit 1
|
||||
@go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./store || exit 1
|
||||
@go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./utils || exit 1
|
||||
@go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./web || exit 1
|
||||
@$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=180s ./api || exit 1
|
||||
@$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=12s ./model || exit 1
|
||||
@$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./store || exit 1
|
||||
@$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./utils || exit 1
|
||||
@$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./web || exit 1
|
||||
|
||||
build:
|
||||
@go build $(GOFLAGS) ./...
|
||||
@$(GO) build $(GOFLAGS) ./...
|
||||
|
||||
install:
|
||||
@go get $(GOFLAGS) github.com/tools/godep
|
||||
|
||||
@if [ $(shell docker ps -a | grep -ci mattermost-mysql) -eq 0 ]; then \
|
||||
echo restoring go libs using godep; \
|
||||
$(GOPATH)/bin/godep restore; \
|
||||
echo starting mattermost-mysql; \
|
||||
docker run --name mattermost-mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=mostest \
|
||||
-e MYSQL_USER=mmuser -e MYSQL_PASSWORD=mostest -e MYSQL_DATABASE=mattermost_test -d mysql > /dev/null; \
|
||||
@@ -64,37 +64,37 @@ install:
|
||||
|
||||
test: install
|
||||
@mkdir -p logs
|
||||
@go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=180s ./api || exit 1
|
||||
@go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=12s ./model || exit 1
|
||||
@go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./store || exit 1
|
||||
@go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./utils || exit 1
|
||||
@go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./web || exit 1
|
||||
@$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=180s ./api || exit 1
|
||||
@$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=12s ./model || exit 1
|
||||
@$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./store || exit 1
|
||||
@$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./utils || exit 1
|
||||
@$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./web || exit 1
|
||||
|
||||
benchmark: install
|
||||
@mkdir -p logs
|
||||
@go test $(GOFLAGS) -test.v -run=NO_TESTS -bench=$(BENCH) ./api || exit 1
|
||||
@$(GO) test $(GOFLAGS) -test.v -run=NO_TESTS -bench=$(BENCH) ./api || exit 1
|
||||
|
||||
cover: install
|
||||
rm -Rf $(DIST_RESULTS)
|
||||
mkdir -p $(DIST_RESULTS)
|
||||
|
||||
@go test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/api.cover.out github.com/mattermost/platform/api
|
||||
@go test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/model.cover.out github.com/mattermost/platform/model
|
||||
@go test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/store.cover.out github.com/mattermost/platform/store
|
||||
@go test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/utils.cover.out github.com/mattermost/platform/utils
|
||||
@go test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/web.cover.out github.com/mattermost/platform/web
|
||||
@$(GO) test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/api.cover.out github.com/mattermost/platform/api
|
||||
@$(GO) test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/model.cover.out github.com/mattermost/platform/model
|
||||
@$(GO) test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/store.cover.out github.com/mattermost/platform/store
|
||||
@$(GO) test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/utils.cover.out github.com/mattermost/platform/utils
|
||||
@$(GO) test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/web.cover.out github.com/mattermost/platform/web
|
||||
|
||||
cd $(DIST_RESULTS) && \
|
||||
echo "mode: set" > coverage.out && cat *.cover.out | grep -v mode: | sort -r | \
|
||||
awk '{if($$1 != last) {print $$0;last=$$1}}' >> coverage.out
|
||||
|
||||
cd $(DIST_RESULTS) && go tool cover -html=coverage.out -o=coverage.html
|
||||
cd $(DIST_RESULTS) && $(GO) tool cover -html=coverage.out -o=coverage.html
|
||||
|
||||
rm -f $(DIST_RESULTS)/*.cover.out
|
||||
|
||||
clean:
|
||||
rm -Rf $(DIST_ROOT)
|
||||
@go clean $(GOFLAGS) -i ./...
|
||||
@$(GO) clean $(GOFLAGS) -i ./...
|
||||
|
||||
@if [ $(shell docker ps -a | grep -ci mattermost-mysql) -eq 1 ]; then \
|
||||
echo stopping mattermost-mysql; \
|
||||
@@ -124,7 +124,7 @@ run: install
|
||||
@cd web/react && npm start &
|
||||
|
||||
@echo starting go web server
|
||||
@go run $(GOFLAGS) mattermost.go -config=config.json &
|
||||
@$(GO) run $(GOFLAGS) mattermost.go -config=config.json &
|
||||
|
||||
@echo starting compass watch
|
||||
@cd web/sass-files && compass watch &
|
||||
@@ -161,8 +161,8 @@ cleandb:
|
||||
|
||||
dist: install
|
||||
|
||||
@go build $(GOFLAGS) -i -a ./...
|
||||
@go install $(GOFLAGS) -a ./...
|
||||
@$(GO) build $(GOFLAGS) -i -a ./...
|
||||
@$(GO) install $(GOFLAGS) -a ./...
|
||||
|
||||
mkdir -p $(DIST_PATH)/bin
|
||||
cp $(GOPATH)/bin/platform $(DIST_PATH)/bin
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
**Mattermost Preview**
|
||||
**Mattermost Alpha**
|
||||
**Team Communication Service**
|
||||
**Version 0.5.0**
|
||||
**Development Build**
|
||||
|
||||
|
||||
About Mattermost
|
||||
@@ -22,7 +22,7 @@ Learn More
|
||||
Installing the Mattermost
|
||||
=========================
|
||||
|
||||
You're installing "Mattermost Preview", a pre-released 0.5.0 version intended for an early look at what we're building. While SpinPunch runs this version internally, it's not recommended for production deployments since we can't guarantee API stability or backwards compatibility until our 1.0.0 version release.
|
||||
You're installing "Mattermost Alpha", a pre-released version intended for an early look at what we're building. While SpinPunch runs this version internally, it's not recommended for production deployments since we can't guarantee API stability or backwards compatibility until our production release.
|
||||
|
||||
That said, any issues at all, please let us know on the Mattermost forum at: http://forum.mattermost.org
|
||||
|
||||
|
||||
@@ -710,6 +710,11 @@ func removeChannelMember(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
message := model.NewMessage(c.Session.TeamId, "", userId, model.ACTION_USER_REMOVED)
|
||||
message.Add("channel_id",id)
|
||||
message.Add("remover", c.Session.UserId)
|
||||
PublishAndForget(message)
|
||||
|
||||
c.LogAudit("name=" + channel.Name + " user_id=" + userId)
|
||||
|
||||
result := make(map[string]string)
|
||||
|
||||
@@ -101,6 +101,12 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set(model.HEADER_REQUEST_ID, c.RequestId)
|
||||
w.Header().Set(model.HEADER_VERSION_ID, utils.Cfg.ServiceSettings.Version)
|
||||
|
||||
// Instruct the browser not to display us in an iframe for anti-clickjacking
|
||||
if !h.isApi {
|
||||
w.Header().Set("X-Frame-Options", "DENY")
|
||||
w.Header().Set("Content-Security-Policy", "frame-ancestors none")
|
||||
}
|
||||
|
||||
sessionId := ""
|
||||
|
||||
// attempt to parse the session token from the header
|
||||
|
||||
@@ -33,7 +33,7 @@ func InitFile(r *mux.Router) {
|
||||
|
||||
sr := r.PathPrefix("/files").Subrouter()
|
||||
sr.Handle("/upload", ApiUserRequired(uploadFile)).Methods("POST")
|
||||
sr.Handle("/get/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+\\.[A-Za-z0-9]{3,}}", ApiAppHandler(getFile)).Methods("GET")
|
||||
sr.Handle("/get/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+(\\.[A-Za-z0-9]{3,})?}", ApiAppHandler(getFile)).Methods("GET")
|
||||
sr.Handle("/get_public_link", ApiUserRequired(getPublicLink)).Methods("POST")
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch
|
||||
go func() {
|
||||
var thumbnail image.Image
|
||||
if imgConfig.Width > int(utils.Cfg.ImageSettings.ThumbnailWidth) {
|
||||
thumbnail = resize.Resize(utils.Cfg.ImageSettings.ThumbnailWidth, utils.Cfg.ImageSettings.ThumbnailHeight, img, resize.NearestNeighbor)
|
||||
thumbnail = resize.Resize(utils.Cfg.ImageSettings.ThumbnailWidth, utils.Cfg.ImageSettings.ThumbnailHeight, img, resize.Lanczos3)
|
||||
} else {
|
||||
thumbnail = img
|
||||
}
|
||||
@@ -164,7 +164,7 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch
|
||||
go func() {
|
||||
var preview image.Image
|
||||
if imgConfig.Width > int(utils.Cfg.ImageSettings.PreviewWidth) {
|
||||
preview = resize.Resize(utils.Cfg.ImageSettings.PreviewWidth, utils.Cfg.ImageSettings.PreviewHeight, img, resize.NearestNeighbor)
|
||||
preview = resize.Resize(utils.Cfg.ImageSettings.PreviewWidth, utils.Cfg.ImageSettings.PreviewHeight, img, resize.Lanczos3)
|
||||
} else {
|
||||
preview = img
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
@@ -32,7 +32,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
@@ -40,7 +40,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
@@ -35,7 +35,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
@@ -32,7 +32,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
@@ -35,7 +35,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
@@ -35,7 +35,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
@@ -38,7 +38,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
@@ -35,7 +35,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<td style="border: 1px solid #ddd;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 20px 30px 10px; text-align:left;">
|
||||
<td style="padding: 20px 20px 10px; text-align:left;">
|
||||
<img src="{{.TeamURL}}/static/images/{{.SiteName}}-logodark.png" width="130px" style="opacity: 0.5" alt="">
|
||||
</td>
|
||||
</tr>
|
||||
@@ -32,7 +32,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: center;color: #AAA; font-size: 13px; padding-bottom: 10px;">
|
||||
<td style="text-align: center;color: #AAA; font-size: 11px; padding-bottom: 10px;">
|
||||
<p style="margin: 25px 0;">
|
||||
<img width="65" src="{{.TeamURL}}/static/images/circles.png" alt="">
|
||||
</p>
|
||||
|
||||
@@ -16,6 +16,7 @@ const (
|
||||
ACTION_VIEWED = "viewed"
|
||||
ACTION_NEW_USER = "new_user"
|
||||
ACTION_USER_ADDED = "user_added"
|
||||
ACTION_USER_REMOVED = "user_removed"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
|
||||
const (
|
||||
SESSION_TOKEN = "MMSID"
|
||||
SESSION_TIME_WEB_IN_DAYS = 365
|
||||
SESSION_TIME_WEB_IN_DAYS = 30
|
||||
SESSION_TIME_WEB_IN_SECS = 60 * 60 * 24 * SESSION_TIME_WEB_IN_DAYS
|
||||
SESSION_TIME_MOBILE_IN_DAYS = 365
|
||||
SESSION_TIME_MOBILE_IN_DAYS = 30
|
||||
SESSION_TIME_MOBILE_IN_SECS = 60 * 60 * 24 * SESSION_TIME_MOBILE_IN_DAYS
|
||||
SESSION_CACHE_IN_SECS = 60 * 10
|
||||
SESSION_CACHE_SIZE = 10000
|
||||
|
||||
@@ -319,6 +319,6 @@ func ClearMentionTags(post string) string {
|
||||
}
|
||||
|
||||
var UrlRegex = regexp.MustCompile(`^((?:[a-z]+:\/\/)?(?:(?:[a-z0-9\-]+\.)+(?:[a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(?:\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(?:\?[a-z0-9+_~\-\.%=&]*)?)?(?:#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(?:\s+|$)$`)
|
||||
var PartialUrlRegex = regexp.MustCompile(`/([A-Za-z0-9]{26})/([A-Za-z0-9]{26})/((?:[A-Za-z0-9]{26})?.+\.[A-Za-z0-9]{3,})`)
|
||||
var PartialUrlRegex = regexp.MustCompile(`/([A-Za-z0-9]{26})/([A-Za-z0-9]{26})/((?:[A-Za-z0-9]{26})?.+(?:\.[A-Za-z0-9]{3,})?)`)
|
||||
|
||||
var SplitRunes = map[rune]bool{',': true, ' ': true, '.': true, '!': true, '?': true, ':': true, ';': true, '\n': true, '<': true, '>': true, '(': true, ')': true, '{': true, '}': true, '[': true, ']': true, '+': true, '/': true, '\\': true}
|
||||
|
||||
@@ -120,7 +120,7 @@ func (s SqlChannelStore) Update(channel *model.Channel) StoreChannel {
|
||||
if count, err := s.GetMaster().Update(channel); err != nil {
|
||||
if IsUniqueConstraintError(err.Error(), "Name", "channels_name_teamid_key") {
|
||||
dupChannel := model.Channel{}
|
||||
s.GetReplica().SelectOne(&dupChannel, "SELECT * FROM Channels WHERE TeamId=? AND Name=? AND DeleteAt > 0", channel.TeamId, channel.Name)
|
||||
s.GetReplica().SelectOne(&dupChannel, "SELECT * FROM Channels WHERE TeamId = :TeamId AND Name= :Name AND DeleteAt > 0", map[string]interface{}{"TeamId": channel.TeamId, "Name": channel.Name})
|
||||
if dupChannel.DeleteAt > 0 {
|
||||
result.Err = model.NewAppError("SqlChannelStore.Update", "A channel with that name was previously created", "id="+channel.Id+", "+err.Error())
|
||||
} else {
|
||||
|
||||
@@ -38,7 +38,7 @@ func NewSqlPostStore(sqlStore *SqlStore) PostStore {
|
||||
func (s SqlPostStore) UpgradeSchemaIfNeeded() {
|
||||
|
||||
// These execs are for upgrading currently created databases to full utf8mb4 compliance
|
||||
// Will be removed as seen fit for upgrading
|
||||
// Will be removed as seen fit for upgrading
|
||||
s.GetMaster().Exec("ALTER TABLE Posts charset=utf8mb4")
|
||||
s.GetMaster().Exec("ALTER TABLE Posts MODIFY COLUMN Message varchar(4000) CHARACTER SET utf8mb4")
|
||||
}
|
||||
@@ -80,14 +80,14 @@ func (s SqlPostStore) Save(post *model.Post) StoreChannel {
|
||||
time := model.GetMillis()
|
||||
|
||||
if post.Type != model.POST_JOIN_LEAVE {
|
||||
s.GetMaster().Exec("UPDATE Channels SET LastPostAt = ?, TotalMsgCount = TotalMsgCount + 1 WHERE Id = ?", time, post.ChannelId)
|
||||
s.GetMaster().Exec("UPDATE Channels SET LastPostAt = :LastPostAt, TotalMsgCount = TotalMsgCount + 1 WHERE Id = :ChannelId", map[string]interface{}{"LastPostAt": time, "ChannelId": post.ChannelId})
|
||||
} else {
|
||||
// don't update TotalMsgCount for unimportant messages so that the channel isn't marked as unread
|
||||
s.GetMaster().Exec("UPDATE Channels SET LastPostAt = ? WHERE Id = ?", time, post.ChannelId)
|
||||
s.GetMaster().Exec("UPDATE Channels SET LastPostAt = :LastPostAt WHERE Id = :ChannelId", map[string]interface{}{"LastPostAt": time, "ChannelId": post.ChannelId})
|
||||
}
|
||||
|
||||
if len(post.RootId) > 0 {
|
||||
s.GetMaster().Exec("UPDATE Posts SET UpdateAt = ? WHERE Id = ?", time, post.RootId)
|
||||
s.GetMaster().Exec("UPDATE Posts SET UpdateAt = :UpdateAt WHERE Id = :RootId", map[string]interface{}{"UpdateAt": time, "RootId": post.RootId})
|
||||
}
|
||||
|
||||
result.Data = post
|
||||
@@ -126,10 +126,10 @@ func (s SqlPostStore) Update(oldPost *model.Post, newMessage string, newHashtags
|
||||
result.Err = model.NewAppError("SqlPostStore.Update", "We couldn't update the Post", "id="+editPost.Id+", "+err.Error())
|
||||
} else {
|
||||
time := model.GetMillis()
|
||||
s.GetMaster().Exec("UPDATE Channels SET LastPostAt = ? WHERE Id = ?", time, editPost.ChannelId)
|
||||
s.GetMaster().Exec("UPDATE Channels SET LastPostAt = :LastPostAt WHERE Id = :ChannelId", map[string]interface{}{"LastPostAt": time, "ChannelId": editPost.ChannelId})
|
||||
|
||||
if len(editPost.RootId) > 0 {
|
||||
s.GetMaster().Exec("UPDATE Posts SET UpdateAt = ? WHERE Id = ?", time, editPost.RootId)
|
||||
s.GetMaster().Exec("UPDATE Posts SET UpdateAt = :UpdateAt WHERE Id = :RootId", map[string]interface{}{"UpdateAt": time, "RootId": editPost.RootId})
|
||||
}
|
||||
|
||||
// mark the old post as deleted
|
||||
|
||||
@@ -224,7 +224,7 @@ func (ss SqlStore) CreateFullTextIndexIfNotExists(indexName string, tableName st
|
||||
func (ss SqlStore) createIndexIfNotExists(indexName string, tableName string, columnName string, fullText bool) {
|
||||
|
||||
if utils.Cfg.SqlSettings.DriverName == "postgres" {
|
||||
_, err := ss.GetMaster().SelectStr("SELECT to_regclass($1)", indexName)
|
||||
_, err := ss.GetMaster().SelectStr("SELECT $1::regclass", indexName)
|
||||
// It should fail if the index does not exist
|
||||
if err == nil {
|
||||
return
|
||||
|
||||
@@ -187,7 +187,7 @@ func (us SqlUserStore) UpdateLastPictureUpdate(userId string) StoreChannel {
|
||||
|
||||
curTime := model.GetMillis()
|
||||
|
||||
if _, err := us.GetMaster().Exec("UPDATE Users SET LastPictureUpdate = ?, UpdateAt = ? WHERE Id = ?", curTime, curTime, userId); err != nil {
|
||||
if _, err := us.GetMaster().Exec("UPDATE Users SET LastPictureUpdate = :Time, UpdateAt = :Time WHERE Id = :UserId", map[string]interface{}{"Time": curTime, "UserId": userId}); err != nil {
|
||||
result.Err = model.NewAppError("SqlUserStore.UpdateUpdateAt", "We couldn't update the update_at", "user_id="+userId)
|
||||
} else {
|
||||
result.Data = userId
|
||||
|
||||
@@ -102,8 +102,9 @@ module.exports = React.createClass({
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 className="modal-title" id="myModalLabel">Active Devices</h4>
|
||||
<h4 className="modal-title" id="myModalLabel">Active Sessions</h4>
|
||||
</div>
|
||||
<p className="session-help-text">Sessions are created when you log in with your email and password to a new browser on a device. Sessions let you use Mattermost for up to 30 days without having to log in again. If you want to log out sooner, use the "Logout" button below to end a session.</p>
|
||||
<div ref="modalBody" className="modal-body">
|
||||
<form role="form">
|
||||
{ activityList }
|
||||
|
||||
@@ -32,7 +32,7 @@ module.exports = React.createClass({
|
||||
post.message = this.state.messageText;
|
||||
|
||||
// if this is a reply, trim off any carets from the beginning of a message
|
||||
if (this.state.rootId && post.message.startsWith("^")) {
|
||||
if (this.state.rootId && post.message[0] === "^") {
|
||||
post.message = post.message.replace(/^\^+\s*/g, "");
|
||||
}
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ module.exports = React.createClass({
|
||||
} else if (i < Constants.MAX_DISPLAY_FILES) {
|
||||
postFiles.push(
|
||||
<div className="post-image__column custom-file" key={fileInfo.name+i}>
|
||||
<a href={fileInfo.path+"."+fileInfo.ext} download={fileInfo.name+"."+fileInfo.ext}>
|
||||
<a href={fileInfo.path + (fileInfo.ext ? "." + fileInfo.ext : "")} download={fileInfo.name + (fileInfo.ext ? "." + fileInfo.ext : "")}>
|
||||
<div className={"file-icon "+utils.getIconClassName(type)}/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
64
web/react/components/removed_from_channel_modal.jsx
Обычный файл
64
web/react/components/removed_from_channel_modal.jsx
Обычный файл
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
var ChannelStore = require('../stores/channel_store.jsx');
|
||||
var UserStore = require('../stores/user_store.jsx');
|
||||
var BrowserStore = require('../stores/browser_store.jsx')
|
||||
var utils = require('../utils/utils.jsx');
|
||||
|
||||
module.exports = React.createClass({
|
||||
handleShow: function() {
|
||||
var newState = {};
|
||||
if(BrowserStore.getItem("channel-removed-state")) {
|
||||
newState = BrowserStore.getItem("channel-removed-state");
|
||||
BrowserStore.removeItem("channel-removed-state");
|
||||
}
|
||||
|
||||
this.setState(newState);
|
||||
},
|
||||
handleClose: function() {
|
||||
var townSquare = ChannelStore.getByName("town-square");
|
||||
utils.switchChannel(townSquare);
|
||||
|
||||
this.setState({channelName: "", remover: ""})
|
||||
},
|
||||
componentDidMount: function() {
|
||||
$(this.getDOMNode()).on('show.bs.modal',this.handleShow);
|
||||
$(this.getDOMNode()).on('hidden.bs.modal',this.handleClose);
|
||||
},
|
||||
componentWillUnmount: function() {
|
||||
$(this.getDOMNode()).off('show.bs.modal',this.handleShow);
|
||||
$(this.getDOMNode()).off('hidden.bs.modal',this.handleClose);
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {channelName: "", remover: ""}
|
||||
},
|
||||
render: function() {
|
||||
var currentUser = UserStore.getCurrentUser();
|
||||
var channelName = this.state.channelName ? this.state.channelName : "the channel"
|
||||
var remover = this.state.remover ? this.state.remover : "Someone"
|
||||
|
||||
if (currentUser != null) {
|
||||
return (
|
||||
<div className="modal fade" ref="modal" id="removed_from_channel" tabIndex="-1" role="dialog" aria-hidden="true">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 className="modal-title">Removed from {channelName}</h4>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<p>{remover} removed you from {channelName}</p>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-primary" data-dismiss="modal">Okay</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return <div/>;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -7,6 +7,7 @@ var AsyncClient = require('../utils/async_client.jsx');
|
||||
var SocketStore = require('../stores/socket_store.jsx');
|
||||
var UserStore = require('../stores/user_store.jsx');
|
||||
var TeamStore = require('../stores/team_store.jsx');
|
||||
var BrowserStore = require('../stores/browser_store.jsx')
|
||||
var utils = require('../utils/utils.jsx');
|
||||
var SidebarHeader = require('./sidebar_header.jsx');
|
||||
var SearchBox = require('./search_bar.jsx');
|
||||
@@ -197,6 +198,19 @@ module.exports = React.createClass({
|
||||
if (UserStore.getCurrentId() === msg.user_id) {
|
||||
AsyncClient.getChannels(true);
|
||||
}
|
||||
} else if(msg.action === "user_removed") {
|
||||
if(msg.user_id === UserStore.getCurrentId()) {
|
||||
AsyncClient.getChannels(true);
|
||||
|
||||
if(msg.props.channel_id === ChannelStore.getCurrentId() && $('#removed_from_channel').length > 0) {
|
||||
var sentState = {};
|
||||
sentState.channelName = ChannelStore.getCurrent().display_name;
|
||||
sentState.remover = UserStore.getProfile(msg.props.remover).username;
|
||||
|
||||
BrowserStore.setItem('channel-removed-state',sentState);
|
||||
$('#removed_from_channel').modal('show');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
updateTitle: function() {
|
||||
|
||||
@@ -36,7 +36,6 @@ module.exports = React.createClass({
|
||||
|
||||
this.resize();
|
||||
this.processMentions();
|
||||
this.updateTextdiv();
|
||||
},
|
||||
componentWillUnmount: function() {
|
||||
PostStore.removeAddMentionListener(this._onChange);
|
||||
@@ -87,7 +86,6 @@ module.exports = React.createClass({
|
||||
this.processMentions();
|
||||
this.doProcessMentions = false;
|
||||
}
|
||||
this.updateTextdiv();
|
||||
this.resize();
|
||||
},
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
@@ -117,17 +115,6 @@ module.exports = React.createClass({
|
||||
});
|
||||
}, 1);
|
||||
},
|
||||
updateTextdiv: function() {
|
||||
var html = utils.insertHtmlEntities(this.refs.message.getDOMNode().value);
|
||||
for (var k in this.mentions) {
|
||||
var m = this.mentions[k];
|
||||
var re = new RegExp('( |^)@' + m + '( |$|\n)', 'm');
|
||||
html = html.replace(re, '$1<span class="mention">@'+m+'</span>$2');
|
||||
}
|
||||
var re2 = new RegExp('(^$)(?![.\n])', 'gm');
|
||||
html = html.replace(re2, '<br/><br/>');
|
||||
$(this.refs.textdiv.getDOMNode()).html(html);
|
||||
},
|
||||
handleChange: function() {
|
||||
this.props.onUserInput(this.refs.message.getDOMNode().value);
|
||||
this.resize();
|
||||
@@ -181,7 +168,7 @@ module.exports = React.createClass({
|
||||
}
|
||||
},
|
||||
processMentions: function() {
|
||||
/* First, find all the possible mentions, highlight them in the HTML and add
|
||||
/* First, find all the possible mentions and add
|
||||
them all to a list of mentions */
|
||||
var text = utils.insertHtmlEntities(this.refs.message.getDOMNode().value);
|
||||
|
||||
@@ -192,9 +179,7 @@ module.exports = React.createClass({
|
||||
var matches = text.match(re1);
|
||||
|
||||
if (!matches) {
|
||||
$(this.refs.textdiv.getDOMNode()).text(text);
|
||||
this.updateMentionTab(null, []);
|
||||
this.mentions = [];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -207,7 +192,7 @@ module.exports = React.createClass({
|
||||
}
|
||||
|
||||
/* Figure out what the user is currently typing. If it's a mention then we don't
|
||||
want to highlight it and add it to the mention list yet, so we remove it if
|
||||
want to add it to the mention list yet, so we remove it if
|
||||
there is only one occurence of that mention so far. */
|
||||
var caret = utils.getCaretPosition(this.refs.message.getDOMNode());
|
||||
|
||||
@@ -225,14 +210,13 @@ module.exports = React.createClass({
|
||||
typingMention = text.substring(atIndex+1, caret);
|
||||
}
|
||||
|
||||
var re3 = new RegExp('@' + typingMention + '( |$|\n)', 'g');
|
||||
var re2 = new RegExp('@' + typingMention + '( |$|\n)', 'g');
|
||||
|
||||
if ((text.match(re3) || []).length === 1 && mentions.indexOf(typingMention) !== -1) {
|
||||
if ((text.match(re2) || []).length === 1 && mentions.indexOf(typingMention) !== -1) {
|
||||
mentions.splice(mentions.indexOf(typingMention), 1);
|
||||
}
|
||||
|
||||
this.updateMentionTab(null, mentions);
|
||||
this.mentions = mentions;
|
||||
},
|
||||
checkForNewMention: function(text) {
|
||||
var caret = utils.getCaretPosition(this.refs.message.getDOMNode());
|
||||
@@ -287,15 +271,9 @@ module.exports = React.createClass({
|
||||
elm.value = cmd;
|
||||
this.handleChange();
|
||||
},
|
||||
scroll: function() {
|
||||
var e = this.refs.message.getDOMNode();
|
||||
var d = this.refs.textdiv.getDOMNode();
|
||||
$(d).scrollTop($(e).scrollTop());
|
||||
},
|
||||
resize: function() {
|
||||
var e = this.refs.message.getDOMNode();
|
||||
var w = this.refs.wrapper.getDOMNode();
|
||||
var d = this.refs.textdiv.getDOMNode();
|
||||
|
||||
var lht = parseInt($(e).css('lineHeight'),10);
|
||||
var lines = e.scrollHeight / lht;
|
||||
@@ -303,15 +281,11 @@ module.exports = React.createClass({
|
||||
|
||||
if (e.scrollHeight - mod < 167) {
|
||||
$(e).css({'height':'auto','overflow-y':'hidden'}).height(e.scrollHeight - mod);
|
||||
$(d).css({'height':'auto','overflow-y':'hidden'}).height(e.scrollHeight - mod);
|
||||
$(w).css({'height':'auto'}).height(e.scrollHeight+2);
|
||||
} else {
|
||||
$(e).css({'height':'auto','overflow-y':'scroll'}).height(167);
|
||||
$(d).css({'height':'auto','overflow-y':'scroll'}).height(167);
|
||||
$(w).css({'height':'auto'}).height(167);
|
||||
}
|
||||
|
||||
$(d).scrollTop($(e).scrollTop());
|
||||
},
|
||||
handleFocus: function() {
|
||||
var elm = this.refs.message.getDOMNode();
|
||||
@@ -332,8 +306,7 @@ module.exports = React.createClass({
|
||||
return (
|
||||
<div ref="wrapper" className="textarea-wrapper">
|
||||
<CommandList ref='commands' addCommand={this.addCommand} channelId={this.props.channelId} />
|
||||
<div className="form-control textarea-div" ref="textdiv"/>
|
||||
<textarea id={this.props.id} ref="message" className={"form-control custom-textarea " + this.state.connection} spellCheck="true" autoComplete="off" autoCorrect="off" rows="1" placeholder={this.props.createMessage} value={this.props.messageText} onInput={this.handleChange} onChange={this.handleChange} onKeyPress={this.handleKeyPress} onKeyDown={this.handleKeyDown} onScroll={this.scroll} onFocus={this.handleFocus} onBlur={this.handleBlur} onPaste={this.handlePaste} />
|
||||
<textarea id={this.props.id} ref="message" className={"form-control custom-textarea " + this.state.connection} spellCheck="true" autoComplete="off" autoCorrect="off" rows="1" placeholder={this.props.createMessage} value={this.props.messageText} onInput={this.handleChange} onChange={this.handleChange} onKeyPress={this.handleKeyPress} onKeyDown={this.handleKeyDown} onFocus={this.handleFocus} onBlur={this.handleBlur} onPaste={this.handlePaste} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -617,7 +617,7 @@ var SecurityTab = React.createClass({
|
||||
<br></br>
|
||||
<a data-toggle="modal" className="security-links theme" data-target="#access-history" href="#" onClick={this.handleHistoryOpen}><i className="fa fa-clock-o"></i>View Access History</a>
|
||||
<b> </b>
|
||||
<a data-toggle="modal" className="security-links theme" data-target="#activity-log" href="#" onClick={this.handleDevicesOpen}><i className="fa fa-globe"></i>View and Logout of Active Devices</a>
|
||||
<a data-toggle="modal" className="security-links theme" data-target="#activity-log" href="#" onClick={this.handleDevicesOpen}><i className="fa fa-globe"></i>View and Logout of Active Sessions</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -34,6 +34,7 @@ var MentionList = require('../components/mention_list.jsx');
|
||||
var ChannelInfoModal = require('../components/channel_info_modal.jsx');
|
||||
var AccessHistoryModal = require('../components/access_history_modal.jsx');
|
||||
var ActivityLogModal = require('../components/activity_log_modal.jsx');
|
||||
var RemovedFromChannelModal = require('../components/removed_from_channel_modal.jsx')
|
||||
|
||||
|
||||
var Constants = require('../utils/constants.jsx');
|
||||
@@ -217,4 +218,9 @@ global.window.setup_channel_page = function(team_name, team_type, team_id, chann
|
||||
document.getElementById('activity_log_modal')
|
||||
);
|
||||
|
||||
React.render(
|
||||
<RemovedFromChannelModal />,
|
||||
document.getElementById('removed_from_channel_modal')
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
@@ -546,10 +546,13 @@ module.exports.getIconClassName = function(fileType) {
|
||||
|
||||
module.exports.splitFileLocation = function(fileLocation) {
|
||||
var fileSplit = fileLocation.split('.');
|
||||
if (fileSplit.length < 2) return {};
|
||||
|
||||
var ext = fileSplit[fileSplit.length-1];
|
||||
fileSplit.splice(fileSplit.length-1,1)
|
||||
var ext = "";
|
||||
if (fileSplit.length > 1) {
|
||||
ext = fileSplit[fileSplit.length - 1];
|
||||
fileSplit.splice(fileSplit.length - 1, 1);
|
||||
}
|
||||
|
||||
var filePath = fileSplit.join('.');
|
||||
var filename = filePath.split('/')[filePath.split('/').length-1];
|
||||
|
||||
|
||||
@@ -28,4 +28,7 @@
|
||||
.report__info {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
.session-help-text {
|
||||
padding: 20px 20px 5px 20px;
|
||||
}
|
||||
@@ -8,7 +8,6 @@
|
||||
padding: 100px 0px 50px 0px;
|
||||
max-width: 380px;
|
||||
margin: 0 auto;
|
||||
font-size: 1.1em;
|
||||
position: relative;
|
||||
h1, h2, h3, h4, h5, h6, p {
|
||||
line-height: 1.3;
|
||||
@@ -17,18 +16,18 @@
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5em;
|
||||
letter-spacing: -0.5px;
|
||||
font-size: em(28px);
|
||||
font-size: em(30px);
|
||||
}
|
||||
h3 {
|
||||
font-weight: 600;
|
||||
margin: 0 0 1.3em 0;
|
||||
font-size: 1.4em;
|
||||
font-size: 1.5em;
|
||||
&.extra-margin {
|
||||
margin-bottom: 2.5em;
|
||||
}
|
||||
}
|
||||
h4 {
|
||||
font-size: em(18px);
|
||||
font-size: em(20px);
|
||||
font-weight: 600;
|
||||
margin-bottom: 1em;
|
||||
&.text--light {
|
||||
@@ -78,6 +77,7 @@
|
||||
width: 33px;
|
||||
top: -10px;
|
||||
position: relative;
|
||||
font-size: em(16px);
|
||||
line-height: 20px;
|
||||
font-weight: 600;
|
||||
background: #fff;
|
||||
@@ -87,7 +87,7 @@
|
||||
.btn {
|
||||
padding: em(7px) em(15px);
|
||||
font-weight: 600;
|
||||
font-size: em(13px);
|
||||
margin-right: 5px;
|
||||
&.btn-custom-login {
|
||||
display: block;
|
||||
min-width: 200px;
|
||||
@@ -126,13 +126,11 @@
|
||||
font-size: 0.9em;
|
||||
}
|
||||
&.glyphicon-chevron-right {
|
||||
margin-left: 0.3em;
|
||||
font-size: 0.8em;
|
||||
right: -2px;
|
||||
top: 0px;
|
||||
}
|
||||
&.glyphicon-chevron-left {
|
||||
margin-right: 0.3em;
|
||||
font-size: 0.8em;
|
||||
left: -2px;
|
||||
top: 0px;
|
||||
|
||||
Двоичные данные
web/static/images/Bladekick-logodark.png
Двоичные данные
web/static/images/Bladekick-logodark.png
Двоичный файл не отображается.
|
До Ширина: | Высота: | Размер: 7.6 KiB После Ширина: | Высота: | Размер: 10 KiB |
Двоичные данные
web/static/images/Mattermost-logodark.png
Двоичные данные
web/static/images/Mattermost-logodark.png
Двоичный файл не отображается.
|
До Ширина: | Высота: | Размер: 7.6 KiB После Ширина: | Высота: | Размер: 10 KiB |
@@ -47,6 +47,7 @@
|
||||
<div id="channel_info_modal"></div>
|
||||
<div id="access_history_modal"></div>
|
||||
<div id="activity_log_modal"></div>
|
||||
<div id="removed_from_channel_modal"></div>
|
||||
<script>
|
||||
window.setup_channel_page('{{ .Props.TeamDisplayName }}', '{{ .Props.TeamType }}', '{{ .Props.TeamId }}', '{{ .Props.ChannelName }}', '{{ .Props.ChannelId }}');
|
||||
</script>
|
||||
|
||||
@@ -36,6 +36,13 @@
|
||||
|
||||
<script type="text/javascript" src="https://cloudfront.loggly.com/js/loggly.tracker.js" async></script>
|
||||
<script id="config" type="text/javascript" src="/static/config/config.js"></script>
|
||||
<style id="antiClickjack">body{display:none !important;}</style>
|
||||
<script type="text/javascript">
|
||||
if (self === top) {
|
||||
var blocker = document.getElementById("antiClickjack");
|
||||
blocker.parentNode.removeChild(blocker);
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
if (config == null) {
|
||||
config = {};
|
||||
|
||||
Ссылка в новой задаче
Block a user