summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.sass-lint.yml178
-rw-r--r--Godeps/Godeps.json4
-rw-r--r--Godeps/_workspace/src/github.com/NYTimes/gziphandler/LICENSE.md13
-rw-r--r--Godeps/_workspace/src/github.com/NYTimes/gziphandler/README.md52
-rw-r--r--Godeps/_workspace/src/github.com/NYTimes/gziphandler/gzip.go140
-rw-r--r--Makefile17
-rw-r--r--api/command.go94
-rw-r--r--api/command_test.go5
-rw-r--r--api/post.go25
-rw-r--r--config/config.json6
-rwxr-xr-xdocker/2.1/docker-entry.sh2
-rw-r--r--i18n/en.json12
-rw-r--r--i18n/es.json8
-rw-r--r--model/client.go7
-rw-r--r--model/command.go33
-rw-r--r--model/config.go19
-rw-r--r--model/push_notification.go4
-rw-r--r--model/utils.go12
-rw-r--r--store/sql_command_store.go1
-rw-r--r--store/sql_user_store.go19
-rw-r--r--store/sql_user_store_test.go81
-rw-r--r--store/store.go2
-rw-r--r--web/web.go16
-rw-r--r--webapp/Makefile19
-rw-r--r--webapp/components/admin_console/email_settings.jsx44
-rw-r--r--webapp/components/center_panel.jsx5
-rw-r--r--webapp/components/code_preview.jsx101
-rw-r--r--webapp/components/more_channels.jsx2
-rw-r--r--webapp/components/new_channel_modal.jsx2
-rw-r--r--webapp/components/popover_list_members.jsx1
-rw-r--r--webapp/components/suggestion/command_provider.jsx4
-rw-r--r--webapp/components/suggestion/suggestion_box.jsx3
-rw-r--r--webapp/components/textbox.jsx1
-rw-r--r--webapp/components/user_settings/manage_command_hooks.jsx313
-rw-r--r--webapp/components/user_settings/user_settings_advanced.jsx4
-rw-r--r--webapp/components/user_settings/user_settings_display.jsx2
-rw-r--r--webapp/components/view_image.jsx17
-rw-r--r--webapp/i18n/en.json10
-rw-r--r--webapp/i18n/es.json6
-rw-r--r--webapp/i18n/pt.json4
-rw-r--r--webapp/package.json6
-rw-r--r--webapp/root.jsx2
-rw-r--r--webapp/sass/base/_structure.scss30
-rw-r--r--webapp/sass/base/_typography.scss12
-rw-r--r--webapp/sass/components/_alerts.scss6
-rw-r--r--webapp/sass/components/_buttons.scss8
-rw-r--r--webapp/sass/components/_emoticons.scss12
-rw-r--r--webapp/sass/components/_error-bar.scss23
-rw-r--r--webapp/sass/components/_files.scss155
-rw-r--r--webapp/sass/components/_inputs.scss21
-rw-r--r--webapp/sass/components/_links.scss17
-rw-r--r--webapp/sass/components/_mentions.scss38
-rw-r--r--webapp/sass/components/_modal.scss269
-rw-r--r--webapp/sass/components/_oauth.scss14
-rw-r--r--webapp/sass/components/_popover.scss99
-rw-r--r--webapp/sass/components/_scrollbar.scss29
-rw-r--r--webapp/sass/components/_search.scss107
-rw-r--r--webapp/sass/components/_suggestion-list.scss20
-rw-r--r--webapp/sass/components/_tooltip.scss4
-rw-r--r--webapp/sass/components/_tutorial.scss115
-rw-r--r--webapp/sass/components/_videos.scss61
-rw-r--r--webapp/sass/layout/_content.scss45
-rw-r--r--webapp/sass/layout/_footer.scss6
-rw-r--r--webapp/sass/layout/_forms.scss20
-rw-r--r--webapp/sass/layout/_headers.scss141
-rw-r--r--webapp/sass/layout/_markdown.scss99
-rw-r--r--webapp/sass/layout/_navigation.scss58
-rw-r--r--webapp/sass/layout/_post-right.scss46
-rw-r--r--webapp/sass/layout/_post.scss480
-rw-r--r--webapp/sass/layout/_sidebar-left.scss111
-rw-r--r--webapp/sass/layout/_sidebar-menu.scss46
-rw-r--r--webapp/sass/layout/_sidebar-right.scss62
-rw-r--r--webapp/sass/layout/_webhooks.scss18
-rw-r--r--webapp/sass/responsive/_desktop.scss8
-rw-r--r--webapp/sass/responsive/_mobile.scss9
-rw-r--r--webapp/sass/responsive/_tablet.scss6
-rw-r--r--webapp/sass/routes/_access-history.scss6
-rw-r--r--webapp/sass/routes/_activity-log.scss22
-rw-r--r--webapp/sass/routes/_admin-console.scss106
-rw-r--r--webapp/sass/routes/_docs.scss6
-rw-r--r--webapp/sass/routes/_error-page.scss12
-rw-r--r--webapp/sass/routes/_loading.scss19
-rw-r--r--webapp/sass/routes/_settings.scss189
-rw-r--r--webapp/sass/routes/_signup.scss128
-rw-r--r--webapp/sass/routes/_statistics.scss45
-rw-r--r--webapp/sass/utils/_animations.scss14
-rw-r--r--webapp/sass/utils/_functions.scss14
-rw-r--r--webapp/sass/utils/_mixins.scss25
-rw-r--r--webapp/sass/utils/_module.scss2
-rw-r--r--webapp/sass/utils/_variables.scss12
-rw-r--r--webapp/utils/async_client.jsx6
-rw-r--r--webapp/utils/client.jsx5
-rw-r--r--webapp/utils/constants.jsx86
-rw-r--r--webapp/utils/markdown.jsx90
-rw-r--r--webapp/utils/syntax_hightlighting.jsx197
-rw-r--r--webapp/utils/text_formatting.jsx5
-rw-r--r--webapp/utils/utils.jsx3
-rw-r--r--webapp/webpack.config.js30
98 files changed, 2916 insertions, 1597 deletions
diff --git a/.sass-lint.yml b/.sass-lint.yml
new file mode 100644
index 000000000..71a68af11
--- /dev/null
+++ b/.sass-lint.yml
@@ -0,0 +1,178 @@
+# sass-lint config generated by make-sass-lint-config v0.1.1
+#
+# The following scss-lint Linters are not yet supported by sass-lint:
+# DisableLinterReason, ElsePlacement, PropertyCount, SelectorDepth
+# SpaceAroundOperator, TrailingWhitespace, UnnecessaryParentReference, Compass::*
+#
+# The following settings/values are unsupported by sass-lint:
+# Linter Indentation, option "allow_non_nested_indentation"
+# Linter Indentation, option "character"
+# Linter NestingDepth, option "ignore_parent_selectors"
+# Linter PropertySortOrder, option "min_properties"
+# Linter PropertySortOrder, option "separate_groups"
+# Linter SpaceBeforeBrace, option "allow_single_line_padding"
+# Linter VendorPrefix, option "identifier_list"
+
+files:
+ include: '**/*.scss'
+options:
+ formatter: stylish
+ merge-default-rules: false
+rules:
+ bem-depth:
+ - 1
+ - max-depth: 3
+ border-zero:
+ - 1
+ - convention: none
+ brace-style:
+ - 2
+ - allow-single-line: true
+ class-name-format:
+ - 1
+ - convention: hyphenatedbem
+ clean-import-paths:
+ - 1
+ - filename-extension: false
+ leading-underscore: false
+ empty-line-between-blocks:
+ - 2
+ - ignore-single-line-rulesets: true
+ extends-before-declarations: 1
+ extends-before-mixins: 1
+ final-newline:
+ - 1
+ - include: true
+ force-attribute-nesting: 1
+ force-element-nesting: 1
+ force-pseudo-nesting: 1
+ function-name-format:
+ - 1
+ - allow-leading-underscore: true
+ convention: hyphenatedlowercase
+ hex-length:
+ - 1
+ - style: short
+ hex-notation:
+ - 1
+ - style: lowercase
+ id-name-format:
+ - 1
+ - convention: hyphenatedbem
+ indentation:
+ - 2
+ - size: 4
+ leading-zero:
+ - 1
+ - include: false
+ mixin-name-format:
+ - 0
+ - allow-leading-underscore: true
+ convention: hyphenatedlowercase
+ mixins-before-declarations: 1
+ nesting-depth:
+ - 1
+ - max-depth: 4
+ no-color-keyword: 1
+ no-color-literals: 1
+ no-css-comments: 1
+ no-debug: 1
+ no-duplicate-properties: 1
+ no-empty-rulesets: 1
+ no-extends: 0
+ no-ids: 1
+ no-important: 1
+ no-invalid-hex: 1
+ no-mergeable-selectors: 1
+ no-misspelled-properties:
+ - 1
+ - extra-properties: ['overflow-scrolling', 'font-smoothing']
+ no-qualifying-elements:
+ - 1
+ - allow-element-with-attribute: false
+ allow-element-with-class: false
+ allow-element-with-id: false
+ no-trailing-zero: 1
+ no-transition-all: 0
+ no-url-protocols: 1
+ no-vendor-prefixes:
+ - 1
+ - additional-identifiers: []
+ excluded-identifiers: []
+ placeholder-in-extend: 1
+ placeholder-name-format:
+ - 1
+ - convention: hyphenatedlowercase
+ property-sort-order:
+ - 1
+ - ignore-custom-properties: false
+ property-units:
+ - 1
+ - global:
+ - ch
+ - em
+ - ex
+ - rem
+ - cm
+ - in
+ - mm
+ - pc
+ - pt
+ - px
+ - q
+ - vh
+ - vw
+ - vmin
+ - vmax
+ - deg
+ - grad
+ - rad
+ - turn
+ - ms
+ - s
+ - Hz
+ - kHz
+ - dpi
+ - dpcm
+ - dppx
+ - '%'
+ per-property: {}
+ quotes:
+ - 1
+ - style: single
+ shorthand-values:
+ - 1
+ - allowed-shorthands:
+ - 1
+ - 2
+ - 3
+ single-line-per-selector: 2
+ space-after-bang:
+ - 2
+ - include: false
+ space-after-colon:
+ - 2
+ - include: true
+ space-after-comma:
+ - 1
+ - include: true
+ space-before-bang:
+ - 2
+ - include: true
+ space-before-brace:
+ - 2
+ - include: true
+ space-before-colon: 1
+ space-between-parens:
+ - 2
+ - include: false
+ trailing-semicolon: 2
+ url-quotes: 1
+ variable-for-property:
+ - 0
+ - properties: []
+ variable-name-format:
+ - 1
+ - allow-leading-underscore: true
+ convention: hyphenatedlowercase
+ zero-unit: 1
diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json
index 808d8dab1..f94cafc1c 100644
--- a/Godeps/Godeps.json
+++ b/Godeps/Godeps.json
@@ -6,6 +6,10 @@
],
"Deps": [
{
+ "ImportPath": "github.com/NYTimes/gziphandler",
+ "Rev": "a88790d49798560db24af70fb6a10a66e2549a72"
+ },
+ {
"ImportPath": "github.com/alecthomas/log4go",
"Rev": "8e9057c3b25c409a34c0b9737cdc82cbcafeabce"
},
diff --git a/Godeps/_workspace/src/github.com/NYTimes/gziphandler/LICENSE.md b/Godeps/_workspace/src/github.com/NYTimes/gziphandler/LICENSE.md
new file mode 100644
index 000000000..b7e2ecb63
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/NYTimes/gziphandler/LICENSE.md
@@ -0,0 +1,13 @@
+Copyright (c) 2015 The New York Times Company
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this library except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/Godeps/_workspace/src/github.com/NYTimes/gziphandler/README.md b/Godeps/_workspace/src/github.com/NYTimes/gziphandler/README.md
new file mode 100644
index 000000000..b1d55e26e
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/NYTimes/gziphandler/README.md
@@ -0,0 +1,52 @@
+Gzip Handler
+============
+
+This is a tiny Go package which wraps HTTP handlers to transparently gzip the
+response body, for clients which support it. Although it's usually simpler to
+leave that to a reverse proxy (like nginx or Varnish), this package is useful
+when that's undesirable.
+
+
+## Usage
+
+Call `GzipHandler` with any handler (an object which implements the
+`http.Handler` interface), and it'll return a new handler which gzips the
+response. For example:
+
+```go
+package main
+
+import (
+ "io"
+ "net/http"
+ "github.com/NYTimes/gziphandler"
+)
+
+func main() {
+ withoutGz := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/plain")
+ io.WriteString(w, "Hello, World")
+ })
+
+ withGz := gziphandler.GzipHandler(withoutGz)
+
+ http.Handle("/", withGz)
+ http.ListenAndServe("0.0.0.0:8000", nil)
+}
+```
+
+
+## Documentation
+
+The docs can be found at [godoc.org] [docs], as usual.
+
+
+## License
+
+[Apache 2.0] [license].
+
+
+
+
+[docs]: https://godoc.org/github.com/nytimes/gziphandler
+[license]: https://github.com/nytimes/gziphandler/blob/master/LICENSE.md
diff --git a/Godeps/_workspace/src/github.com/NYTimes/gziphandler/gzip.go b/Godeps/_workspace/src/github.com/NYTimes/gziphandler/gzip.go
new file mode 100644
index 000000000..d0c85c6d3
--- /dev/null
+++ b/Godeps/_workspace/src/github.com/NYTimes/gziphandler/gzip.go
@@ -0,0 +1,140 @@
+package gziphandler
+
+import (
+ "compress/gzip"
+ "fmt"
+ "net/http"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+const (
+ vary = "Vary"
+ acceptEncoding = "Accept-Encoding"
+ contentEncoding = "Content-Encoding"
+)
+
+type codings map[string]float64
+
+// The default qvalue to assign to an encoding if no explicit qvalue is set.
+// This is actually kind of ambiguous in RFC 2616, so hopefully it's correct.
+// The examples seem to indicate that it is.
+const DEFAULT_QVALUE = 1.0
+
+var gzipWriterPool = sync.Pool{
+ New: func() interface{} { return gzip.NewWriter(nil) },
+}
+
+// GzipResponseWriter provides an http.ResponseWriter interface, which gzips
+// bytes before writing them to the underlying response. This doesn't set the
+// Content-Encoding header, nor close the writers, so don't forget to do that.
+type GzipResponseWriter struct {
+ gw *gzip.Writer
+ http.ResponseWriter
+}
+
+// Write appends data to the gzip writer.
+func (w GzipResponseWriter) Write(b []byte) (int, error) {
+ return w.gw.Write(b)
+}
+
+// Flush flushes the underlying *gzip.Writer and then the underlying
+// http.ResponseWriter if it is an http.Flusher. This makes GzipResponseWriter
+// an http.Flusher.
+func (w GzipResponseWriter) Flush() {
+ w.gw.Flush()
+ if fw, ok := w.ResponseWriter.(http.Flusher); ok {
+ fw.Flush()
+ }
+}
+
+// GzipHandler wraps an HTTP handler, to transparently gzip the response body if
+// the client supports it (via the Accept-Encoding header).
+func GzipHandler(h http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Add(vary, acceptEncoding)
+
+ if acceptsGzip(r) {
+ // Bytes written during ServeHTTP are redirected to this gzip writer
+ // before being written to the underlying response.
+ gzw := gzipWriterPool.Get().(*gzip.Writer)
+ defer gzipWriterPool.Put(gzw)
+ gzw.Reset(w)
+ defer gzw.Close()
+
+ w.Header().Set(contentEncoding, "gzip")
+ h.ServeHTTP(GzipResponseWriter{gzw, w}, r)
+ } else {
+ h.ServeHTTP(w, r)
+ }
+ })
+}
+
+// acceptsGzip returns true if the given HTTP request indicates that it will
+// accept a gzippped response.
+func acceptsGzip(r *http.Request) bool {
+ acceptedEncodings, _ := parseEncodings(r.Header.Get(acceptEncoding))
+ return acceptedEncodings["gzip"] > 0.0
+}
+
+// parseEncodings attempts to parse a list of codings, per RFC 2616, as might
+// appear in an Accept-Encoding header. It returns a map of content-codings to
+// quality values, and an error containing the errors encounted. It's probably
+// safe to ignore those, because silently ignoring errors is how the internet
+// works.
+//
+// See: http://tools.ietf.org/html/rfc2616#section-14.3
+func parseEncodings(s string) (codings, error) {
+ c := make(codings)
+ e := make([]string, 0)
+
+ for _, ss := range strings.Split(s, ",") {
+ coding, qvalue, err := parseCoding(ss)
+
+ if err != nil {
+ e = append(e, err.Error())
+
+ } else {
+ c[coding] = qvalue
+ }
+ }
+
+ // TODO (adammck): Use a proper multi-error struct, so the individual errors
+ // can be extracted if anyone cares.
+ if len(e) > 0 {
+ return c, fmt.Errorf("errors while parsing encodings: %s", strings.Join(e, ", "))
+ }
+
+ return c, nil
+}
+
+// parseCoding parses a single conding (content-coding with an optional qvalue),
+// as might appear in an Accept-Encoding header. It attempts to forgive minor
+// formatting errors.
+func parseCoding(s string) (coding string, qvalue float64, err error) {
+ for n, part := range strings.Split(s, ";") {
+ part = strings.TrimSpace(part)
+ qvalue = DEFAULT_QVALUE
+
+ if n == 0 {
+ coding = strings.ToLower(part)
+
+ } else if strings.HasPrefix(part, "q=") {
+ qvalue, err = strconv.ParseFloat(strings.TrimPrefix(part, "q="), 64)
+
+ if qvalue < 0.0 {
+ qvalue = 0.0
+
+ } else if qvalue > 1.0 {
+ qvalue = 1.0
+ }
+ }
+ }
+
+ if coding == "" {
+ err = fmt.Errorf("empty content-coding")
+ }
+
+ return
+}
diff --git a/Makefile b/Makefile
index 3ab7ae267..1d78118e4 100644
--- a/Makefile
+++ b/Makefile
@@ -138,6 +138,7 @@ package:
tar -C dist -czf $(DIST_PATH).tar.gz mattermost
build-client:
+ mkdir -p webapp/dist/files
cd webapp && make build
go-test:
@@ -198,15 +199,10 @@ clean: stop-docker
rm -Rf $(DIST_ROOT)
go clean $(GOFLAGS) -i ./...
- rm -rf web/react/node_modules
- rm -f web/static/js/bundle*.js
- rm -f web/static/js/bundle*.js.map
- rm -f web/static/js/libs*.js
- rm -f web/static/css/styles.css
+ cd webapp && make clean
rm -rf api/data
rm -rf logs
- rm -rf web/sass-files/.sass-cache
rm -rf Godeps/_workspace/pkg/
@@ -222,14 +218,17 @@ nuke: | clean clean-docker
touch $@
-run: start-docker run-server run-client
+run: | start-docker run-client run-server
run-server: .prepare-go
@echo Starting go web server
$(GO) run $(GOFLAGS) mattermost.go -config=config.json &
-run-client: build-client
- @echo Starting react processo
+run-client:
+ @echo Starting client
+
+ mkdir -p webapp/dist/files
+ cd webapp && make run
@if [ "$(BUILD_ENTERPRISE)" = "true" ] && [ -d "$(ENTERPRISE_DIR)" ]; then \
cp ./config/config.json ./config/config.json.bak; \
diff --git a/api/command.go b/api/command.go
index 99fd05d7a..29cee070e 100644
--- a/api/command.go
+++ b/api/command.go
@@ -44,7 +44,7 @@ func InitCommand(r *mux.Router) {
sr := r.PathPrefix("/commands").Subrouter()
sr.Handle("/execute", ApiUserRequired(executeCommand)).Methods("POST")
- sr.Handle("/list", ApiUserRequired(listCommands)).Methods("GET")
+ sr.Handle("/list", ApiUserRequired(listCommands)).Methods("POST")
sr.Handle("/create", ApiUserRequired(createCommand)).Methods("POST")
sr.Handle("/list_team_commands", ApiUserRequired(listTeamCommands)).Methods("GET")
@@ -76,7 +76,9 @@ func listCommands(c *Context, w http.ResponseWriter, r *http.Request) {
} else {
teamCmds := result.Data.([]*model.Command)
for _, cmd := range teamCmds {
- if cmd.AutoComplete && !seen[cmd.Id] {
+ if cmd.ExternalManagement {
+ commands = append(commands, autocompleteCommands(c, cmd, r)...)
+ } else if cmd.AutoComplete && !seen[cmd.Id] {
cmd.Sanitize()
seen[cmd.Trigger] = true
commands = append(commands, cmd)
@@ -88,6 +90,92 @@ func listCommands(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(model.CommandListToJson(commands)))
}
+func autocompleteCommands(c *Context, cmd *model.Command, r *http.Request) []*model.Command {
+ props := model.MapFromJson(r.Body)
+ command := strings.TrimSpace(props["command"])
+ channelId := strings.TrimSpace(props["channelId"])
+ parts := strings.Split(command, " ")
+ trigger := parts[0][1:]
+ message := strings.Join(parts[1:], " ")
+
+ chanChan := Srv.Store.Channel().Get(channelId)
+ teamChan := Srv.Store.Team().Get(c.Session.TeamId)
+ userChan := Srv.Store.User().Get(c.Session.UserId)
+
+ var team *model.Team
+ if tr := <-teamChan; tr.Err != nil {
+ c.Err = tr.Err
+ return make([]*model.Command, 0, 32)
+ } else {
+ team = tr.Data.(*model.Team)
+ }
+
+ var user *model.User
+ if ur := <-userChan; ur.Err != nil {
+ c.Err = ur.Err
+ return make([]*model.Command, 0, 32)
+ } else {
+ user = ur.Data.(*model.User)
+ }
+
+ var channel *model.Channel
+ if cr := <-chanChan; cr.Err != nil {
+ c.Err = cr.Err
+ return make([]*model.Command, 0, 32)
+ } else {
+ channel = cr.Data.(*model.Channel)
+ }
+
+ l4g.Debug(fmt.Sprintf(utils.T("api.command.execute_command.debug"), trigger, c.Session.UserId))
+ p := url.Values{}
+ p.Set("token", cmd.Token)
+
+ p.Set("team_id", cmd.TeamId)
+ p.Set("team_domain", team.Name)
+
+ p.Set("channel_id", channelId)
+ p.Set("channel_name", channel.Name)
+
+ p.Set("user_id", c.Session.UserId)
+ p.Set("user_name", user.Username)
+
+ p.Set("command", "/"+trigger)
+ p.Set("text", message)
+ p.Set("response_url", "not supported yet")
+ p.Set("suggest", "true")
+
+ method := "POST"
+ if cmd.Method == model.COMMAND_METHOD_GET {
+ method = "GET"
+ }
+
+ tr := &http.Transport{
+ TLSClientConfig: &tls.Config{InsecureSkipVerify: *utils.Cfg.ServiceSettings.EnableInsecureOutgoingConnections},
+ }
+ client := &http.Client{Transport: tr}
+
+ req, _ := http.NewRequest(method, cmd.URL, strings.NewReader(p.Encode()))
+ req.Header.Set("Accept", "application/json")
+ if cmd.Method == model.COMMAND_METHOD_POST {
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ }
+
+ if resp, err := client.Do(req); err != nil {
+ c.Err = model.NewLocAppError("command", "api.command.execute_command.failed.app_error", map[string]interface{}{"Trigger": trigger}, err.Error())
+ } else {
+ if resp.StatusCode == http.StatusOK {
+ response := model.CommandListFromJson(resp.Body)
+
+ return response
+
+ } else {
+ body, _ := ioutil.ReadAll(resp.Body)
+ c.Err = model.NewLocAppError("command", "api.command.execute_command.failed_resp.app_error", map[string]interface{}{"Trigger": trigger, "Status": resp.Status}, string(body))
+ }
+ }
+ return make([]*model.Command, 0, 32)
+}
+
func executeCommand(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(r.Body)
command := strings.TrimSpace(props["command"])
@@ -159,7 +247,7 @@ func executeCommand(c *Context, w http.ResponseWriter, r *http.Request) {
teamCmds := result.Data.([]*model.Command)
for _, cmd := range teamCmds {
- if trigger == cmd.Trigger {
+ if trigger == cmd.Trigger || cmd.ExternalManagement {
l4g.Debug(fmt.Sprintf(utils.T("api.command.execute_command.debug"), trigger, c.Session.UserId))
p := url.Values{}
diff --git a/api/command_test.go b/api/command_test.go
index 22e2bd666..8ca8b65b1 100644
--- a/api/command_test.go
+++ b/api/command_test.go
@@ -24,7 +24,10 @@ func TestListCommands(t *testing.T) {
Client.LoginByEmail(team.Name, user1.Email, "pwd")
- if results, err := Client.ListCommands(); err != nil {
+ channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
+
+ if results, err := Client.ListCommands(channel1.Id, "/test"); err != nil {
t.Fatal(err)
} else {
commands := results.Data.([]*model.Command)
diff --git a/api/post.go b/api/post.go
index d0ec5826a..36fd4ee79 100644
--- a/api/post.go
+++ b/api/post.go
@@ -670,8 +670,15 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
alreadySeen[session.DeviceId] = session.DeviceId
msg := model.PushNotification{}
- msg.Badge = 1
+ if badge := <-Srv.Store.User().GetUnreadCount(id); badge.Err != nil {
+ msg.Badge = 1
+ l4g.Error(utils.T("store.sql_user.get_unread_count.app_error"), id, badge.Err)
+ } else {
+ msg.Badge = int(badge.Data.(int64))
+ }
msg.ServerId = utils.CfgDiagnosticId
+ msg.ChannelId = channel.Id
+ msg.ChannelName = channel.Name
if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") {
msg.Platform = model.PUSH_NOTIFY_APPLE
@@ -681,10 +688,20 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":")
}
- if channel.Type == model.CHANNEL_DIRECT {
- msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_message")
+ if *utils.Cfg.EmailSettings.PushNotificationContents == model.FULL_NOTIFICATION {
+ if channel.Type == model.CHANNEL_DIRECT {
+ msg.Category = model.CATEGORY_DM
+ msg.Message = "@" + senderName + ": " + model.ClearMentionTags(post.Message)
+ } else {
+ msg.Message = "@" + senderName + " @ " + channelName + ": " + model.ClearMentionTags(post.Message)
+ }
} else {
- msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_mention") + channelName
+ if channel.Type == model.CHANNEL_DIRECT {
+ msg.Category = model.CATEGORY_DM
+ msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_message")
+ } else {
+ msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_mention") + channelName
+ }
}
tr := &http.Transport{
diff --git a/config/config.json b/config/config.json
index 57f455b32..a0b50cbf2 100644
--- a/config/config.json
+++ b/config/config.json
@@ -21,7 +21,8 @@
"SessionLengthSSOInDays": 30,
"SessionCacheInMinutes": 10,
"WebsocketSecurePort": 443,
- "WebsocketPort": 80
+ "WebsocketPort": 80,
+ "WebserverMode": "regular"
},
"TeamSettings": {
"SiteName": "Mattermost",
@@ -86,6 +87,7 @@
"InviteSalt": "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9YoS",
"PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5eL",
"SendPushNotifications": false,
+ "PushNotificationContents": "generic",
"PushNotificationServer": ""
},
"RateLimitSettings": {
@@ -139,4 +141,4 @@
"IdAttribute": null,
"QueryTimeout": 60
}
-} \ No newline at end of file
+}
diff --git a/docker/2.1/docker-entry.sh b/docker/2.1/docker-entry.sh
index 6bd2a1263..14b9ccd9d 100755
--- a/docker/2.1/docker-entry.sh
+++ b/docker/2.1/docker-entry.sh
@@ -107,5 +107,5 @@ sleep 5
# ------------------------
echo starting platform
-cd /mattermost/bin
+cd /mattermost/mattermost/bin
./platform -config=/config_docker.json
diff --git a/i18n/en.json b/i18n/en.json
index 0c32d8dc3..25a435580 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -3076,14 +3076,14 @@
"translation": "We encountered an error finding the account"
},
{
- "id": "store.sql_user.get_by_auth.other.app_error",
- "translation": "We encountered an error trying to find the account by authentication type."
- },
- {
"id": "store.sql_user.get_by_auth.missing_account.app_error",
"translation": "We couldn't find an existing account matching your authentication type for this team. This team may require an invite from the team owner to join."
},
{
+ "id": "store.sql_user.get_by_auth.other.app_error",
+ "translation": "We encountered an error trying to find the account by authentication type."
+ },
+ {
"id": "store.sql_user.get_by_username.app_error",
"translation": "We couldn't find an existing account matching your username for this team. This team may require an invite from the team owner to join."
},
@@ -3108,6 +3108,10 @@
"translation": "We could not count the users"
},
{
+ "id": "store.sql_user.get_unread_count.app_error",
+ "translation": "We could not get the unread message count for the user"
+ },
+ {
"id": "store.sql_user.missing_account.const",
"translation": "We couldn't find an existing account matching your email address for this team. This team may require an invite from the team owner to join."
},
diff --git a/i18n/es.json b/i18n/es.json
index 4622a1b12..231ebd9fb 100644
--- a/i18n/es.json
+++ b/i18n/es.json
@@ -3080,6 +3080,10 @@
"translation": "No pudimos encontrar una cuenta existente que coincida con tu tipo de autenticación para este equipo. Es posible que necesites una invitación por parte del dueño del equipo para unirte."
},
{
+ "id": "store.sql_user.get_by_auth.other.app_error",
+ "translation": "Encontramos un error tratando de identificar la cuenta con este tipo de autenticación."
+ },
+ {
"id": "store.sql_user.get_by_username.app_error",
"translation": "No pudimos encontrar una cuenta existente que coincida con tu nombre de usuario para este equipo. Es posible que necesites una invitación por parte del dueño del equipo para unirte."
},
@@ -3104,6 +3108,10 @@
"translation": "No pudimos contar los usuarios"
},
{
+ "id": "store.sql_user.get_unread_count.app_error",
+ "translation": "No pudimos obtener la cantidad de mensajes sin leer del usuario"
+ },
+ {
"id": "store.sql_user.missing_account.const",
"translation": "No pudimos encontrar una cuenta existente que coincida con tu dirección de correo electrónico para este equipo. Es posible que necesites una invitación del dueño del equipo para poder unirte."
},
diff --git a/model/client.go b/model/client.go
index 3adcb980d..14c175fc1 100644
--- a/model/client.go
+++ b/model/client.go
@@ -363,8 +363,11 @@ func (c *Client) Command(channelId string, command string, suggest bool) (*Resul
}
}
-func (c *Client) ListCommands() (*Result, *AppError) {
- if r, err := c.DoApiGet("/commands/list", "", ""); err != nil {
+func (c *Client) ListCommands(channelId string, command string) (*Result, *AppError) {
+ m := make(map[string]string)
+ m["command"] = command
+ m["channelId"] = channelId
+ if r, err := c.DoApiPost("/commands/list", MapToJson(m)); err != nil {
return nil, err
} else {
return &Result{r.Header.Get(HEADER_REQUEST_ID),
diff --git a/model/command.go b/model/command.go
index 56d88f13c..8e0b31583 100644
--- a/model/command.go
+++ b/model/command.go
@@ -14,22 +14,23 @@ const (
)
type Command struct {
- Id string `json:"id"`
- Token string `json:"token"`
- CreateAt int64 `json:"create_at"`
- UpdateAt int64 `json:"update_at"`
- DeleteAt int64 `json:"delete_at"`
- CreatorId string `json:"creator_id"`
- TeamId string `json:"team_id"`
- Trigger string `json:"trigger"`
- Method string `json:"method"`
- Username string `json:"username"`
- IconURL string `json:"icon_url"`
- AutoComplete bool `json:"auto_complete"`
- AutoCompleteDesc string `json:"auto_complete_desc"`
- AutoCompleteHint string `json:"auto_complete_hint"`
- DisplayName string `json:"display_name"`
- URL string `json:"url"`
+ Id string `json:"id"`
+ Token string `json:"token"`
+ CreateAt int64 `json:"create_at"`
+ UpdateAt int64 `json:"update_at"`
+ DeleteAt int64 `json:"delete_at"`
+ CreatorId string `json:"creator_id"`
+ TeamId string `json:"team_id"`
+ ExternalManagement bool `json:"external_management"`
+ Trigger string `json:"trigger"`
+ Method string `json:"method"`
+ Username string `json:"username"`
+ IconURL string `json:"icon_url"`
+ AutoComplete bool `json:"auto_complete"`
+ AutoCompleteDesc string `json:"auto_complete_desc"`
+ AutoCompleteHint string `json:"auto_complete_hint"`
+ DisplayName string `json:"display_name"`
+ URL string `json:"url"`
}
func (o *Command) ToJson() string {
diff --git a/model/config.go b/model/config.go
index 82c51224e..d684c72b2 100644
--- a/model/config.go
+++ b/model/config.go
@@ -21,6 +21,13 @@ const (
SERVICE_GITLAB = "gitlab"
SERVICE_GOOGLE = "google"
+
+ WEBSERVER_MODE_REGULAR = "regular"
+ WEBSERVER_MODE_GZIP = "gzip"
+ WEBSERVER_MODE_DISABLED = "disabled"
+
+ GENERIC_NOTIFICATION = "generic"
+ FULL_NOTIFICATION = "full"
)
type ServiceSettings struct {
@@ -46,6 +53,7 @@ type ServiceSettings struct {
SessionCacheInMinutes *int
WebsocketSecurePort *int
WebsocketPort *int
+ WebserverMode *string
}
type SSOSettings struct {
@@ -116,6 +124,7 @@ type EmailSettings struct {
PasswordResetSalt string
SendPushNotifications *bool
PushNotificationServer *string
+ PushNotificationContents *string
}
type RateLimitSettings struct {
@@ -294,6 +303,11 @@ func (o *Config) SetDefaults() {
*o.EmailSettings.PushNotificationServer = ""
}
+ if o.EmailSettings.PushNotificationContents == nil {
+ o.EmailSettings.PushNotificationContents = new(string)
+ *o.EmailSettings.PushNotificationContents = GENERIC_NOTIFICATION
+ }
+
if o.SupportSettings.TermsOfServiceLink == nil {
o.SupportSettings.TermsOfServiceLink = new(string)
*o.SupportSettings.TermsOfServiceLink = "/static/help/terms.html"
@@ -383,6 +397,11 @@ func (o *Config) SetDefaults() {
o.ServiceSettings.AllowCorsFrom = new(string)
*o.ServiceSettings.AllowCorsFrom = ""
}
+
+ if o.ServiceSettings.WebserverMode == nil {
+ o.ServiceSettings.WebserverMode = new(string)
+ *o.ServiceSettings.WebserverMode = "regular"
+ }
}
func (o *Config) IsValid() *AppError {
diff --git a/model/push_notification.go b/model/push_notification.go
index 76f5bd125..9196a44dd 100644
--- a/model/push_notification.go
+++ b/model/push_notification.go
@@ -11,6 +11,8 @@ import (
const (
PUSH_NOTIFY_APPLE = "apple"
PUSH_NOTIFY_ANDROID = "android"
+
+ CATEGORY_DM = "DIRECT_MESSAGE"
)
type PushNotification struct {
@@ -22,6 +24,8 @@ type PushNotification struct {
Message string `json:"message"`
Badge int `json:"badge"`
ContentAvailable int `json:"cont_ava"`
+ ChannelId string `json:"channel_id"`
+ ChannelName string `json:"channel_name"`
}
func (me *PushNotification) ToJson() string {
diff --git a/model/utils.go b/model/utils.go
index 808c89e30..1ce41bb30 100644
--- a/model/utils.go
+++ b/model/utils.go
@@ -367,3 +367,15 @@ func IsValidHttpUrl(rawUrl string) bool {
return true
}
+
+func IsValidHttpsUrl(rawUrl string) bool {
+ if strings.Index(rawUrl, "https://") != 0 {
+ return false
+ }
+
+ if _, err := url.ParseRequestURI(rawUrl); err != nil {
+ return false
+ }
+
+ return true
+}
diff --git a/store/sql_command_store.go b/store/sql_command_store.go
index 074a6e588..a35737bd7 100644
--- a/store/sql_command_store.go
+++ b/store/sql_command_store.go
@@ -34,6 +34,7 @@ func NewSqlCommandStore(sqlStore *SqlStore) CommandStore {
}
func (s SqlCommandStore) UpgradeSchemaIfNeeded() {
+ s.CreateColumnIfNotExists("Commands", "ExternalManagement", "tinyint(1)", "boolean", "0")
}
func (s SqlCommandStore) CreateIndexesIfNotExists() {
diff --git a/store/sql_user_store.go b/store/sql_user_store.go
index cc6829b94..6062b8a6a 100644
--- a/store/sql_user_store.go
+++ b/store/sql_user_store.go
@@ -652,3 +652,22 @@ func (us SqlUserStore) AnalyticsUniqueUserCount(teamId string) StoreChannel {
return storeChannel
}
+
+func (us SqlUserStore) GetUnreadCount(userId string) StoreChannel {
+ storeChannel := make(StoreChannel)
+
+ go func() {
+ result := StoreResult{}
+
+ if count, err := us.GetReplica().SelectInt("SELECT SUM(CASE WHEN c.Type = 'D' THEN (c.TotalMsgCount - cm.MsgCount) ELSE 0 END + cm.MentionCount) FROM Channels c INNER JOIN ChannelMembers cm ON cm.ChannelId = c.Id AND cm.UserId = :UserId", map[string]interface{}{"UserId": userId}); err != nil {
+ result.Err = model.NewLocAppError("SqlUserStore.GetMentionCount", "store.sql_user.get_unread_count.app_error", nil, err.Error())
+ } else {
+ result.Data = count
+ }
+
+ storeChannel <- result
+ close(storeChannel)
+ }()
+
+ return storeChannel
+}
diff --git a/store/sql_user_store_test.go b/store/sql_user_store_test.go
index 2350bad30..8f2366136 100644
--- a/store/sql_user_store_test.go
+++ b/store/sql_user_store_test.go
@@ -421,3 +421,84 @@ func TestUserStoreUpdateAuthData(t *testing.T) {
}
}
}
+
+func TestUserUnreadCount(t *testing.T) {
+ Setup()
+
+ teamId := model.NewId()
+
+ c1 := model.Channel{}
+ c1.TeamId = teamId
+ c1.DisplayName = "Unread Messages"
+ c1.Name = "unread-messages"
+ c1.Type = model.CHANNEL_OPEN
+
+ c2 := model.Channel{}
+ c2.TeamId = teamId
+ c2.DisplayName = "Unread Direct"
+ c2.Name = "unread-direct"
+ c2.Type = model.CHANNEL_DIRECT
+
+ u1 := model.User{}
+ u1.Email = model.NewId()
+ u1.Username = "user1"
+ u1.TeamId = teamId
+ Must(store.User().Save(&u1))
+
+ u2 := model.User{}
+ u2.Email = model.NewId()
+ u2.Username = "user2"
+ u2.TeamId = teamId
+ Must(store.User().Save(&u2))
+
+ if err := (<-store.Channel().Save(&c1)).Err; err != nil {
+ t.Fatal("couldn't save item", err)
+ }
+
+ m1 := model.ChannelMember{}
+ m1.ChannelId = c1.Id
+ m1.UserId = u1.Id
+ m1.NotifyProps = model.GetDefaultChannelNotifyProps()
+
+ m2 := model.ChannelMember{}
+ m2.ChannelId = c1.Id
+ m2.UserId = u2.Id
+ m2.NotifyProps = model.GetDefaultChannelNotifyProps()
+
+ Must(store.Channel().SaveMember(&m1))
+ Must(store.Channel().SaveMember(&m2))
+
+ m1.ChannelId = c2.Id
+ m2.ChannelId = c2.Id
+
+ if err := (<-store.Channel().SaveDirectChannel(&c2, &m1, &m2)).Err; err != nil {
+ t.Fatal("couldn't save direct channel", err)
+ }
+
+ p1 := model.Post{}
+ p1.ChannelId = c1.Id
+ p1.UserId = u1.Id
+ p1.Message = "this is a message for @" + u2.Username
+
+ // Post one message with mention to open channel
+ Must(store.Post().Save(&p1))
+ Must(store.Channel().IncrementMentionCount(c1.Id, u2.Id))
+
+ // Post 2 messages without mention to direct channel
+ p2 := model.Post{}
+ p2.ChannelId = c2.Id
+ p2.UserId = u1.Id
+ p2.Message = "first message"
+ Must(store.Post().Save(&p2))
+
+ p3 := model.Post{}
+ p3.ChannelId = c2.Id
+ p3.UserId = u1.Id
+ p3.Message = "second message"
+ Must(store.Post().Save(&p3))
+
+ badge := (<-store.User().GetUnreadCount(u2.Id)).Data.(int64)
+ if badge != 3 {
+ t.Fatal("should have 3 unread messages")
+ }
+}
diff --git a/store/store.go b/store/store.go
index b041cfa25..1738ba84e 100644
--- a/store/store.go
+++ b/store/store.go
@@ -130,6 +130,8 @@ type UserStore interface {
GetSystemAdminProfiles() StoreChannel
PermanentDelete(userId string) StoreChannel
AnalyticsUniqueUserCount(teamId string) StoreChannel
+
+ GetUnreadCount(userId string) StoreChannel
}
type SessionStore interface {
diff --git a/web/web.go b/web/web.go
index 86b642f3b..ff5040a4b 100644
--- a/web/web.go
+++ b/web/web.go
@@ -7,6 +7,8 @@ import (
"net/http"
"strings"
+ "github.com/NYTimes/gziphandler"
+
l4g "github.com/alecthomas/log4go"
"github.com/mattermost/platform/api"
"github.com/mattermost/platform/model"
@@ -23,11 +25,17 @@ func InitWeb() {
mainrouter := api.Srv.Router
- staticDir := utils.FindDir(CLIENT_DIR)
- l4g.Debug("Using client directory at %v", staticDir)
- mainrouter.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir))))
+ if *utils.Cfg.ServiceSettings.WebserverMode != "disabled" {
+ staticDir := utils.FindDir(CLIENT_DIR)
+ l4g.Debug("Using client directory at %v", staticDir)
+ if *utils.Cfg.ServiceSettings.WebserverMode == "gzip" {
+ mainrouter.PathPrefix("/static/").Handler(gziphandler.GzipHandler(http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir)))))
+ } else {
+ mainrouter.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(staticDir))))
+ }
- mainrouter.Handle("/{anything:.*}", api.AppHandlerIndependent(root)).Methods("GET")
+ mainrouter.Handle("/{anything:.*}", api.AppHandlerIndependent(root)).Methods("GET")
+ }
}
var browsersNotSupported string = "MSIE/8;MSIE/9;MSIE/10;Internet Explorer/8;Internet Explorer/9;Internet Explorer/10;Safari/7;Safari/8"
diff --git a/webapp/Makefile b/webapp/Makefile
index 99f896e53..cd6dfbbad 100644
--- a/webapp/Makefile
+++ b/webapp/Makefile
@@ -1,4 +1,4 @@
-.PHONY: build test
+.PHONY: build test run clean
test:
@echo Checking for style guide compliance
@@ -12,7 +12,20 @@ test:
touch $@
-build: .npminstall
- @echo Building mattermost web client
+build: | .npminstall test
+ @echo Building mattermost Webapp
npm run build
+
+run: .npminstall
+ @echo Running mattermost Webapp for development
+
+ npm run run
+
+
+clean:
+ @echo Cleaning Webapp
+
+ rm -rf dist
+ rm -rf node_modules
+ rm -f .npminstall
diff --git a/webapp/components/admin_console/email_settings.jsx b/webapp/components/admin_console/email_settings.jsx
index 1decdae91..e591b636d 100644
--- a/webapp/components/admin_console/email_settings.jsx
+++ b/webapp/components/admin_console/email_settings.jsx
@@ -58,6 +58,14 @@ var holders = defineMessages({
id: 'admin.email.pushServerEx',
defaultMessage: 'E.g.: "http://push-test.mattermost.com"'
},
+ genericPush: {
+ id: 'admin.email.genericPushNotification',
+ defaultMessage: 'Send generic description with user and channel names'
+ },
+ fullPush: {
+ id: 'admin.email.fullPushNotification',
+ defaultMessage: 'Send full message snippet'
+ },
testing: {
id: 'admin.email.testing',
defaultMessage: 'Testing...'
@@ -87,7 +95,8 @@ class EmailSettings extends React.Component {
saveNeeded: false,
serverError: null,
emailSuccess: null,
- emailFail: null
+ emailFail: null,
+ pushNotificationContents: this.props.config.EmailSettings.PushNotificationContents
};
}
@@ -125,6 +134,7 @@ class EmailSettings extends React.Component {
config.EmailSettings.FeedbackEmail = ReactDOM.findDOMNode(this.refs.feedbackEmail).value.trim();
config.EmailSettings.SMTPServer = ReactDOM.findDOMNode(this.refs.SMTPServer).value.trim();
config.EmailSettings.PushNotificationServer = ReactDOM.findDOMNode(this.refs.PushNotificationServer).value.trim();
+ config.EmailSettings.PushNotificationContents = ReactDOM.findDOMNode(this.refs.PushNotificationContents).value;
config.EmailSettings.SMTPPort = ReactDOM.findDOMNode(this.refs.SMTPPort).value.trim();
config.EmailSettings.SMTPUsername = ReactDOM.findDOMNode(this.refs.SMTPUsername).value.trim();
config.EmailSettings.SMTPPassword = ReactDOM.findDOMNode(this.refs.SMTPPassword).value.trim();
@@ -929,6 +939,38 @@ class EmailSettings extends React.Component {
</div>
<div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='pushNotificationContents'
+ >
+ <FormattedMessage
+ id='admin.email.pushContentTitle'
+ defaultMessage='Push Notification Contents:'
+ />
+ </label>
+ <div className='col-sm-8'>
+ <select
+ className='form-control'
+ id='pushNotificationContents'
+ ref='PushNotificationContents'
+ defaultValue={this.props.config.EmailSettings.PushNotificationContents}
+ onChange={this.handleChange.bind(this, 'pushNotificationContents')}
+ disabled={!this.state.sendPushNotifications}
+ >
+ <option value='generic'>{formatMessage(holders.genericPush)}</option>
+ <option value='full'>{formatMessage(holders.fullPush)}</option>
+ </select>
+ <p className='help-text'>
+ <FormattedHTMLMessage
+ id='admin.email.pushContentDesc'
+ defaultMessage='Selecting "Send generic description with user and channel names" provides push notifications with generic messages, including names of users and channels but no specific details from the message text.<br /><br />
+ Selecting "Send full message snippet" sends excerpts from messages triggering notifications with specifics and may include confidential information sent in messages. If your Push Notification Service is outside your firewall, it is HIGHLY RECOMMENDED this option only be used with an "https" protocol to encrypt the connection.'
+ />
+ </p>
+ </div>
+ </div>
+
+ <div className='form-group'>
<div className='col-sm-12'>
{serverError}
<button
diff --git a/webapp/components/center_panel.jsx b/webapp/components/center_panel.jsx
index 17e5e43d9..6c156f2a8 100644
--- a/webapp/components/center_panel.jsx
+++ b/webapp/components/center_panel.jsx
@@ -120,7 +120,10 @@ export default class CenterPanel extends React.Component {
id='app-content'
className='app__content'
>
- <div id='channel-header'>
+ <div
+ id='channel-header'
+ className='channel-header'
+ >
<ChannelHeader
user={this.state.user}
/>
diff --git a/webapp/components/code_preview.jsx b/webapp/components/code_preview.jsx
new file mode 100644
index 000000000..e769ae590
--- /dev/null
+++ b/webapp/components/code_preview.jsx
@@ -0,0 +1,101 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import $ from 'jquery';
+import * as syntaxHightlighting from 'utils/syntax_hightlighting.jsx';
+import Constants from 'utils/constants.jsx';
+import FileInfoPreview from './file_info_preview.jsx';
+
+import React from 'react';
+
+export default class CodePreview extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.updateStateFromProps = this.updateStateFromProps.bind(this);
+ this.handleReceivedError = this.handleReceivedError.bind(this);
+ this.handleReceivedCode = this.handleReceivedCode.bind(this);
+
+ this.state = {
+ code: '',
+ lang: '',
+ loading: true,
+ success: true
+ };
+ }
+
+ componentDidMount() {
+ this.updateStateFromProps(this.props);
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (this.props.fileUrl !== nextProps.fileUrl) {
+ this.updateStateFromProps(nextProps);
+ }
+ }
+
+ updateStateFromProps(props) {
+ var usedLanguage = syntaxHightlighting.getLang(props.filename);
+
+ if (!usedLanguage || props.fileInfo.size > Constants.CODE_PREVIEW_MAX_FILE_SIZE) {
+ this.setState({code: '', lang: '', loading: false, success: false});
+ return;
+ }
+
+ this.setState({code: '', lang: usedLanguage, loading: true});
+
+ $.ajax({
+ async: true,
+ url: props.fileUrl,
+ type: 'GET',
+ error: this.handleReceivedError,
+ success: this.handleReceivedCode
+ });
+ }
+
+ handleReceivedCode(data) {
+ const parsed = syntaxHightlighting.formatCode(this.state.lang, data, this.props.filename);
+ this.setState({code: parsed, loading: false, success: true});
+ }
+
+ handleReceivedError() {
+ this.setState({loading: false, success: false});
+ }
+
+ static support(filename) {
+ return typeof syntaxHightlighting.getLang(filename) !== 'undefined';
+ }
+
+ render() {
+ if (this.state.loading) {
+ return (
+ <div className='view-image__loading'>
+ <img
+ className='loader-image'
+ src='/static/images/load.gif'
+ />
+ </div>
+ );
+ }
+
+ if (!this.state.success) {
+ return (
+ <FileInfoPreview
+ filename={this.props.filename}
+ fileUrl={this.props.fileUrl}
+ fileInfo={this.props.fileInfo}
+ formatMessage={this.props.formatMessage}
+ />
+ );
+ }
+
+ return <div dangerouslySetInnerHTML={{__html: this.state.code}}/>;
+ }
+}
+
+CodePreview.propTypes = {
+ filename: React.PropTypes.string.isRequired,
+ fileUrl: React.PropTypes.string.isRequired,
+ fileInfo: React.PropTypes.object.isRequired,
+ formatMessage: React.PropTypes.func.isRequired
+};
diff --git a/webapp/components/more_channels.jsx b/webapp/components/more_channels.jsx
index fc047eaff..d0eeec1ef 100644
--- a/webapp/components/more_channels.jsx
+++ b/webapp/components/more_channels.jsx
@@ -160,7 +160,7 @@ export default class MoreChannels extends React.Component {
return (
<div
- className='modal fade'
+ className='modal fade more-channel__modal'
id='more_channels'
ref='modal'
tabIndex='-1'
diff --git a/webapp/components/new_channel_modal.jsx b/webapp/components/new_channel_modal.jsx
index 628687f27..f69eeed49 100644
--- a/webapp/components/new_channel_modal.jsx
+++ b/webapp/components/new_channel_modal.jsx
@@ -38,7 +38,7 @@ class NewChannelModal extends React.Component {
}
componentDidMount() {
if (Utils.isBrowserIE()) {
- $('body').addClass('browser--IE');
+ $('body').addClass('browser--ie');
}
}
handleSubmit(e) {
diff --git a/webapp/components/popover_list_members.jsx b/webapp/components/popover_list_members.jsx
index 81760eb6e..7d048019c 100644
--- a/webapp/components/popover_list_members.jsx
+++ b/webapp/components/popover_list_members.jsx
@@ -158,6 +158,7 @@ export default class PopoverListMembers extends React.Component {
<Popover
ref='memebersPopover'
title={title}
+ className='member-list__popover'
id='member-list-popover'
>
<div className='more-modal__list'>{popoverHtml}</div>
diff --git a/webapp/components/suggestion/command_provider.jsx b/webapp/components/suggestion/command_provider.jsx
index 36860fa66..204f52483 100644
--- a/webapp/components/suggestion/command_provider.jsx
+++ b/webapp/components/suggestion/command_provider.jsx
@@ -37,9 +37,9 @@ CommandSuggestion.propTypes = {
};
export default class CommandProvider {
- handlePretextChanged(suggestionId, pretext) {
+ handlePretextChanged(suggestionId, pretext, channelId) {
if (pretext.startsWith('/')) {
- AsyncClient.getSuggestedCommands(pretext, suggestionId, CommandSuggestion);
+ AsyncClient.getSuggestedCommands(pretext, channelId, suggestionId, CommandSuggestion);
}
}
}
diff --git a/webapp/components/suggestion/suggestion_box.jsx b/webapp/components/suggestion/suggestion_box.jsx
index e3ec63194..97c6c6cd9 100644
--- a/webapp/components/suggestion/suggestion_box.jsx
+++ b/webapp/components/suggestion/suggestion_box.jsx
@@ -111,7 +111,7 @@ export default class SuggestionBox extends React.Component {
handlePretextChanged(pretext) {
for (const provider of this.props.providers) {
- provider.handlePretextChanged(this.suggestionId, pretext);
+ provider.handlePretextChanged(this.suggestionId, pretext, this.props.channelId);
}
}
@@ -160,6 +160,7 @@ SuggestionBox.propTypes = {
value: React.PropTypes.string.isRequired,
onUserInput: React.PropTypes.func,
providers: React.PropTypes.arrayOf(React.PropTypes.object),
+ channelId: React.PropTypes.string,
// explicitly name any input event handlers we override and need to manually call
onChange: React.PropTypes.func,
diff --git a/webapp/components/textbox.jsx b/webapp/components/textbox.jsx
index 1a395072e..952026ed5 100644
--- a/webapp/components/textbox.jsx
+++ b/webapp/components/textbox.jsx
@@ -224,6 +224,7 @@ export default class Textbox extends React.Component {
style={{visibility: this.state.preview ? 'hidden' : 'visible'}}
listComponent={SuggestionList}
providers={this.suggestionProviders}
+ channelId={this.props.channelId}
/>
<div
ref='preview'
diff --git a/webapp/components/user_settings/manage_command_hooks.jsx b/webapp/components/user_settings/manage_command_hooks.jsx
index ce353ad64..9703664cc 100644
--- a/webapp/components/user_settings/manage_command_hooks.jsx
+++ b/webapp/components/user_settings/manage_command_hooks.jsx
@@ -4,9 +4,13 @@
import LoadingScreen from '../loading_screen.jsx';
import * as Client from 'utils/client.jsx';
+import * as Utils from 'utils/utils.jsx';
+import Constants from 'utils/constants.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl';
+const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES;
+
const holders = defineMessages({
requestTypePost: {
id: 'user.settings.cmds.request_type_post',
@@ -59,6 +63,7 @@ export default class ManageCommandCmds extends React.Component {
this.getCmds = this.getCmds.bind(this);
this.addNewCmd = this.addNewCmd.bind(this);
this.emptyCmd = this.emptyCmd.bind(this);
+ this.updateExternalManagement = this.updateExternalManagement.bind(this);
this.updateTrigger = this.updateTrigger.bind(this);
this.updateURL = this.updateURL.bind(this);
this.updateMethod = this.updateMethod.bind(this);
@@ -99,7 +104,7 @@ export default class ManageCommandCmds extends React.Component {
addNewCmd(e) {
e.preventDefault();
- if (this.state.cmd.trigger === '' || this.state.cmd.url === '') {
+ if (this.state.cmd.url === '' || (this.state.cmd.trigger === '' && !this.state.external_management)) {
return;
}
@@ -189,6 +194,12 @@ export default class ManageCommandCmds extends React.Component {
);
}
+ updateExternalManagement(e) {
+ var cmd = this.state.cmd;
+ cmd.external_management = e.target.checked;
+ this.setState(cmd);
+ }
+
updateTrigger(e) {
var cmd = this.state.cmd;
cmd.trigger = e.target.value;
@@ -270,11 +281,26 @@ export default class ManageCommandCmds extends React.Component {
);
}
+ let slashCommandAutocompleteDiv;
+ if (Utils.isFeatureEnabled(PreReleaseFeatures.SLASHCMD_AUTOCMP)) {
+ slashCommandAutocompleteDiv = (
+ <div className='padding-top x2'>
+ <strong>
+ <FormattedMessage
+ id='user.settings.cmds.external_management'
+ defaultMessage='External management: '
+ />
+ </strong><span className='word-break--all'>{cmd.external_management ? this.props.intl.formatMessage(holders.autocompleteYes) : this.props.intl.formatMessage(holders.autocompleteNo)}</span>
+ </div>
+ );
+ }
+
cmds.push(
<div
key={cmd.id}
className='webhook__item webcmd__item'
>
+ {slashCommandAutocompleteDiv}
{triggerDiv}
<div className='padding-top x2 webcmd__url'>
<strong>
@@ -416,43 +442,115 @@ export default class ManageCommandCmds extends React.Component {
</div>
);
- const disableButton = this.state.cmd.trigger === '' || this.state.cmd.url === '';
+ const disableButton = this.state.cmd.url === '' || (this.state.cmd.trigger === '' && !this.state.external_management);
- return (
- <div key='addCommandCmd'>
- <FormattedHTMLMessage
- id='user.settings.cmds.add_desc'
- defaultMessage='Create slash commands to send events to external integrations and receive a response. For example typing `/patient Joe Smith` could bring back search results from your internal health records management system for the name “Joe Smith”. Please see <a href="http://docs.mattermost.com/developer/slash-commands.html">Slash commands documentation</a> for detailed instructions. View all slash commands configured on this team below.'
- />
- <div><label className='control-label padding-top x2'>
- <FormattedMessage
- id='user.settings.cmds.add_new'
- defaultMessage='Add a new command'
- />
- </label></div>
- <div className='padding-top divider-light'></div>
- <div className='padding-top'>
+ let triggerInput;
+ if (!this.state.cmd.external_management) {
+ triggerInput = (
+ <div className='padding-top x2'>
+ <label className='control-label'>
+ <FormattedMessage
+ id='user.settings.cmds.trigger'
+ defaultMessage='Command Trigger Word: '
+ />
+ </label>
+ <div className='padding-top'>
+ <input
+ ref='trigger'
+ className='form-control'
+ value={this.state.cmd.trigger}
+ onChange={this.updateTrigger}
+ placeholder={this.props.intl.formatMessage(holders.addTriggerPlaceholder)}
+ />
+ </div>
+ <div className='padding-top'>
+ <FormattedMessage
+ id='user.settings.cmds.trigger_desc'
+ defaultMessage='Examples: /patient, /client, /employee Reserved: /echo, /join, /logout, /me, /shrug'
+ />
+ </div>
+ </div>
+ );
+ }
+ let slashCommandAutocompleteCheckbox;
+ if (Utils.isFeatureEnabled(PreReleaseFeatures.SLASHCMD_AUTOCMP)) {
+ slashCommandAutocompleteCheckbox = (
+ <div className='padding-top x2'>
+ <label className='control-label'>
+ <FormattedMessage
+ id='user.settings.cmds.external_management'
+ defaultMessage='External management: '
+ />
+ </label>
+ <div className='padding-top'>
+ <div className='checkbox'>
+ <label>
+ <input
+ type='checkbox'
+ checked={this.state.cmd.external_management}
+ onChange={this.updateExternalManagement}
+ />
+ <FormattedMessage
+ id='user.settings.cmds.slashCmd_autocmp'
+ defaultMessage='Enable external application to offer autocomplete'
+ />
+ </label>
+ </div>
+ </div>
+ </div>
+
+ );
+ }
+
+ let autoCompleteSettings;
+ if (!this.state.cmd.external_management) {
+ autoCompleteSettings = (
+ <div>
<div className='padding-top x2'>
<label className='control-label'>
<FormattedMessage
- id='user.settings.cmds.trigger'
- defaultMessage='Command Trigger Word: '
+ id='user.settings.cmds.auto_complete'
+ defaultMessage='Autocomplete: '
+ />
+ </label>
+ <div className='padding-top'>
+ <div className='checkbox'>
+ <label>
+ <input
+ type='checkbox'
+ checked={this.state.cmd.auto_complete}
+ onChange={this.updateAutoComplete}
+ />
+ <FormattedMessage
+ id='user.settings.cmds.auto_complete_help'
+ defaultMessage=' Show this command in the autocomplete list.'
+ />
+ </label>
+ </div>
+ </div>
+ </div>
+
+ <div className='padding-top x2'>
+ <label className='control-label'>
+ <FormattedMessage
+ id='user.settings.cmds.auto_complete_hint'
+ defaultMessage='Autocomplete Hint: '
/>
</label>
<div className='padding-top'>
<input
- ref='trigger'
+ ref='autoCompleteHint'
className='form-control'
- value={this.state.cmd.trigger}
- onChange={this.updateTrigger}
- placeholder={this.props.intl.formatMessage(holders.addTriggerPlaceholder)}
+ value={this.state.cmd.auto_complete_hint}
+ onChange={this.updateAutoCompleteHint}
+ placeholder={this.props.intl.formatMessage(holders.addAutoCompleteHintPlaceholder)}
/>
</div>
<div className='padding-top'>
<FormattedMessage
- id='user.settings.cmds.trigger_desc'
- defaultMessage='Examples: /patient, /client, /employee Reserved: /echo, /join, /logout, /me, /shrug'
+ id='user.settings.cmds.auto_complete_hint_desc'
+ defaultMessage='Optional hint in the autocomplete list about parameters needed for command.'
/>
</div>
</div>
@@ -460,6 +558,76 @@ export default class ManageCommandCmds extends React.Component {
<div className='padding-top x2'>
<label className='control-label'>
<FormattedMessage
+ id='user.settings.cmds.auto_complete_desc'
+ defaultMessage='Autocomplete Description: '
+ />
+ </label>
+ <div className='padding-top'>
+ <input
+ ref='autoCompleteDesc'
+ className='form-control'
+ value={this.state.cmd.auto_complete_desc}
+ onChange={this.updateAutoCompleteDesc}
+ placeholder={this.props.intl.formatMessage(holders.addAutoCompleteDescPlaceholder)}
+ />
+ </div>
+ <div className='padding-top'>
+ <FormattedMessage
+ id='user.settings.cmds.auto_complete_desc_desc'
+ defaultMessage='Optional short description of slash command for the autocomplete list.'
+ />
+ </div>
+ </div>
+
+ <div className='padding-top x2'>
+ <label className='control-label'>
+ <FormattedMessage
+ id='user.settings.cmds.display_name'
+ defaultMessage='Descriptive Label: '
+ />
+ </label>
+ <div className='padding-top'>
+ <input
+ ref='displayName'
+ className='form-control'
+ value={this.state.cmd.display_name}
+ onChange={this.updateDisplayName}
+ placeholder={this.props.intl.formatMessage(holders.addDisplayNamePlaceholder)}
+ />
+ </div>
+ <div className='padding-top'>
+ <FormattedMessage
+ id='user.settings.cmds.cmd_display_name'
+ defaultMessage='Brief description of slash command to show in listings.'
+ />
+ </div>
+ {addError}
+ </div>
+ </div>
+ );
+ }
+
+ return (
+ <div key='addCommandCmd'>
+ <FormattedHTMLMessage
+ id='user.settings.cmds.add_desc'
+ defaultMessage='Create slash commands to send events to external integrations and receive a response. For example typing `/patient Joe Smith` could bring back search results from your internal health records management system for the name “Joe Smith”. Please see <a href="http://docs.mattermost.com/developer/slash-commands.html">Slash commands documentation</a> for detailed instructions. View all slash commands configured on this team below.'
+ />
+ <div><label className='control-label padding-top x2'>
+ <FormattedMessage
+ id='user.settings.cmds.add_new'
+ defaultMessage='Add a new command'
+ />
+ </label></div>
+ <div className='padding-top divider-light'></div>
+ <div className='padding-top'>
+
+ {slashCommandAutocompleteCheckbox}
+ {triggerInput}
+
+ <div className='padding-top x2'>
+ <label className='control-label'>
+ <FormattedMessage
id='user.settings.cmds.url'
defaultMessage='Request URL: '
/>
@@ -560,102 +728,7 @@ export default class ManageCommandCmds extends React.Component {
</div>
</div>
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.auto_complete'
- defaultMessage='Autocomplete: '
- />
- </label>
- <div className='padding-top'>
- <div className='checkbox'>
- <label>
- <input
- type='checkbox'
- checked={this.state.cmd.auto_complete}
- onChange={this.updateAutoComplete}
- />
- <FormattedMessage
- id='user.settings.cmds.auto_complete_help'
- defaultMessage=' Show this command in the autocomplete list.'
- />
- </label>
- </div>
- </div>
- </div>
-
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.auto_complete_hint'
- defaultMessage='Autocomplete Hint: '
- />
- </label>
- <div className='padding-top'>
- <input
- ref='autoCompleteHint'
- className='form-control'
- value={this.state.cmd.auto_complete_hint}
- onChange={this.updateAutoCompleteHint}
- placeholder={this.props.intl.formatMessage(holders.addAutoCompleteHintPlaceholder)}
- />
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.cmds.auto_complete_hint_desc'
- defaultMessage='Optional hint in the autocomplete list about parameters needed for command.'
- />
- </div>
- </div>
-
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.auto_complete_desc'
- defaultMessage='Autocomplete Description: '
- />
- </label>
- <div className='padding-top'>
- <input
- ref='autoCompleteDesc'
- className='form-control'
- value={this.state.cmd.auto_complete_desc}
- onChange={this.updateAutoCompleteDesc}
- placeholder={this.props.intl.formatMessage(holders.addAutoCompleteDescPlaceholder)}
- />
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.cmds.auto_complete_desc_desc'
- defaultMessage='Optional short description of slash command for the autocomplete list.'
- />
- </div>
- </div>
-
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.display_name'
- defaultMessage='Descriptive Label: '
- />
- </label>
- <div className='padding-top'>
- <input
- ref='displayName'
- className='form-control'
- value={this.state.cmd.display_name}
- onChange={this.updateDisplayName}
- placeholder={this.props.intl.formatMessage(holders.addDisplayNamePlaceholder)}
- />
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.cmds.cmd_display_name'
- defaultMessage='Brief description of slash command to show in listings.'
- />
- </div>
- {addError}
- </div>
+ {autoCompleteSettings}
<div className='padding-top x2 padding-bottom'>
<a
diff --git a/webapp/components/user_settings/user_settings_advanced.jsx b/webapp/components/user_settings/user_settings_advanced.jsx
index 7c496f57b..40897e8c9 100644
--- a/webapp/components/user_settings/user_settings_advanced.jsx
+++ b/webapp/components/user_settings/user_settings_advanced.jsx
@@ -51,6 +51,10 @@ const holders = defineMessages({
EMBED_TOGGLE: {
id: 'user.settings.advance.embed_toggle',
defaultMessage: 'Show toggle for all embed previews'
+ },
+ SLASHCMD_AUTOCMP: {
+ id: 'user.settings.advance.slashCmd_autocmp',
+ defaultMessage: 'Enable external application to offer slash command autocomplete'
}
});
diff --git a/webapp/components/user_settings/user_settings_display.jsx b/webapp/components/user_settings/user_settings_display.jsx
index 58d4493cb..3299588f7 100644
--- a/webapp/components/user_settings/user_settings_display.jsx
+++ b/webapp/components/user_settings/user_settings_display.jsx
@@ -195,7 +195,7 @@ export default class UserSettingsDisplay extends React.Component {
const showUsername = (
<FormattedMessage
id='user.settings.display.showUsername'
- defaultMessage='Show username (team default)'
+ defaultMessage='Show username (default)'
/>
);
const showNickname = (
diff --git a/webapp/components/view_image.jsx b/webapp/components/view_image.jsx
index e739fca30..7572f88ae 100644
--- a/webapp/components/view_image.jsx
+++ b/webapp/components/view_image.jsx
@@ -7,6 +7,7 @@ import * as Client from 'utils/client.jsx';
import * as Utils from 'utils/utils.jsx';
import AudioVideoPreview from './audio_video_preview.jsx';
import Constants from 'utils/constants.jsx';
+import CodePreview from './code_preview.jsx';
import FileInfoPreview from './file_info_preview.jsx';
import FileStore from 'stores/file_store.jsx';
import ViewImagePopoverBar from './view_image_popover_bar.jsx';
@@ -254,6 +255,15 @@ class ViewImageModal extends React.Component {
formatMessage={this.props.intl.formatMessage}
/>
);
+ } else if (CodePreview.support(filename)) {
+ content = (
+ <CodePreview
+ filename={filename}
+ fileUrl={fileUrl}
+ fileInfo={fileInfo}
+ formatMessage={this.props.intl.formatMessage}
+ />
+ );
} else {
content = (
<FileInfoPreview
@@ -311,18 +321,19 @@ class ViewImageModal extends React.Component {
<Modal
show={this.props.show}
onHide={this.props.onModalDismissed}
- className='image_modal'
+ className='modal-image'
dialogClassName='modal-image'
>
<Modal.Body
- modalClassName='image-body'
+ modalClassName='modal-image__body'
onClick={this.props.onModalDismissed}
>
<div
- className={'image-wrapper'}
+ className={'modal-image__wrapper'}
onClick={this.props.onModalDismissed}
>
<div
+ className='modal-back'
onMouseEnter={this.onMouseEnterImage}
onMouseLeave={this.onMouseLeaveImage}
onClick={(e) => e.stopPropagation()}
diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json
index bb1f05c23..dc43cc019 100644
--- a/webapp/i18n/en.json
+++ b/webapp/i18n/en.json
@@ -42,6 +42,8 @@
"admin.email.emailSettings": "Email Settings",
"admin.email.emailSuccess": "No errors were reported while sending an email. Please check your inbox to make sure.",
"admin.email.false": "false",
+ "admin.email.fullPushNotification": "Send full message snippet",
+ "admin.email.genericPushNotification": "Send generic description with user and channel names",
"admin.email.inviteSaltDescription": "32-character salt added to signing of email invites. Randomly generated on install. Click \"Re-Generate\" to create new salt.",
"admin.email.inviteSaltExample": "Ex \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"",
"admin.email.inviteSaltTitle": "Invite Salt:",
@@ -56,6 +58,8 @@
"admin.email.passwordSaltDescription": "32-character salt added to signing of password reset emails. Randomly generated on install. Click \"Re-Generate\" to create new salt.",
"admin.email.passwordSaltExample": "Ex \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"",
"admin.email.passwordSaltTitle": "Password Reset Salt:",
+ "admin.email.pushContentDesc": "Selecting \"Send generic description with user and channel names\" provides push notifications with generic messages, including names of users and channels but no specific details from the message text.<br /><br />Selecting \"Send full message snippet\" sends excerpts from messages triggering notifications with specifics and may include confidential information sent in messages. If your Push Notification Service is outside your firewall, it is HIGHLY RECOMMENDED this option only be used with an \"https\" protocol to encrypt the connection.",
+ "admin.email.pushContentTitle": "Push Notification Contents:",
"admin.email.pushDesc": "Typically set to true in production. When true, Mattermost attempts to send iOS and Android push notifications through the push notification server.",
"admin.email.pushServerDesc": "Location of Mattermost push notification service you can set up behind your firewall using https://github.com/mattermost/push-proxy. For testing you can use http://push-test.mattermost.com, which connects to the sample Mattermost iOS app in the public Apple AppStore. Please do not use test service for production deployments.",
"admin.email.pushServerEx": "E.g.: \"http://push-test.mattermost.com\"",
@@ -1091,6 +1095,7 @@
"tutorial_tip.seen": "Seen this before? ",
"upload_overlay.info": "Drop a file to upload it.",
"user.settings.advance.embed_preview": "Show preview snippet of links below message",
+ "user.settings.advance.slashCmd_autocmp": "Enable external application to offer slash command autocomplete",
"user.settings.advance.embed_toggle": "Show toggle for all embed previews",
"user.settings.advance.enabled": "enabled",
"user.settings.advance.feature": " Feature ",
@@ -1138,6 +1143,7 @@
"user.settings.cmds.url_desc": "The callback URL to receive the HTTP POST or GET event request when the slash command is run.",
"user.settings.cmds.username": "Response Username: ",
"user.settings.cmds.username_desc": "Choose a username override for responses for this slash command. Usernames can consist of up to 22 characters consisting of lowercase letters, numbers and they symbols \"-\", \"_\", and \".\" .",
+ "user.settings.cmds.slashCmd_autocmp": "Enable external application to offer autocomplete",
"user.settings.custom_theme.awayIndicator": "Away Indicator",
"user.settings.custom_theme.buttonBg": "Button BG",
"user.settings.custom_theme.buttonColor": "Button Text",
@@ -1174,7 +1180,7 @@
"user.settings.display.preferTime": "Select how you prefer time displayed.",
"user.settings.display.showFullname": "Show first and last name",
"user.settings.display.showNickname": "Show nickname if one exists, otherwise show first and last name",
- "user.settings.display.showUsername": "Show username (team default)",
+ "user.settings.display.showUsername": "Show username (default)",
"user.settings.display.teammateDisplay": "Teammate Name Display",
"user.settings.display.theme.customTheme": "Custom Theme",
"user.settings.display.theme.describe": "Open to manage your theme",
@@ -1313,4 +1319,4 @@
"web.footer.terms": "Terms",
"web.header.back": "Back",
"web.root.singup_info": "All team communication in one place, searchable and accessible anywhere"
-}
+} \ No newline at end of file
diff --git a/webapp/i18n/es.json b/webapp/i18n/es.json
index 0f927101d..457752b64 100644
--- a/webapp/i18n/es.json
+++ b/webapp/i18n/es.json
@@ -42,6 +42,8 @@
"admin.email.emailSettings": "Configuraciones de correo",
"admin.email.emailSuccess": "No fueron reportados errores mientras se enviada el correo. Favor validar en tu bandeja de entrada.",
"admin.email.false": "falso",
+ "admin.email.fullPushNotification": "Enviar el mensaje completo",
+ "admin.email.genericPushNotification": "Enviar descripción generica con nombres de usuario y canal",
"admin.email.inviteSaltDescription": "32-caracter salt añadido a la firma de invitación de correos. Aleatoriamente generado en la instalación. Click \"Re-Generar\" para crear nuevo salt.",
"admin.email.inviteSaltExample": "Ej \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"",
"admin.email.inviteSaltTitle": "Salt de las invitaciones:",
@@ -56,6 +58,8 @@
"admin.email.passwordSaltDescription": "Un salt de 32-caracteres es añadido a la firma de correos para restablecer la contraseña. Aleatoriamente generado en la instalación. Pincha \"Regenerar\" para crear un nuevo salt.",
"admin.email.passwordSaltExample": "Ej \"bjlSR4QqkXFBr7TP4oDzlfZmcNuH9Yo\"",
"admin.email.passwordSaltTitle": "Resetear el Salt para las contraseñas:",
+ "admin.email.pushContentDesc": "Al seleccionar \"Enviar descripción generica con nombres de usuario y canal\" las notificaciones se enviarán con mensajes genericos, incluyendo el nombre del usuario y el canal pero obviando el texto del mensaje.<br /><br /> Al seleccionar \"Enviar el mensaje completo\" envía los mensajes desencadenados por las notificaciones con características específicas y puede incluir información confidencial enviada en los mensajes. En el caso de que tu servidor de Envío de Notificaciones se encuentre fuera del alcance de tu cortafuego,, es ALTAMENTE RECOMENDABLE utilizar está opción junto con el protocolo \"https\" para cifrar las notificaciones enviadas por el servidor.",
+ "admin.email.pushContentTitle": "Contenido de las Notificaciones:",
"admin.email.pushDesc": "Normalmente se asigna como verdadero en producción. Cuando está en verdadero, Mattermost intenta enviar notificaciones a dispositivos iOS y Android a través del servidor de notificaciones.",
"admin.email.pushServerDesc": "Ubicación del servicio de notificaciones push de Mattermost, puedes ubicarlo detras de un cortafuego utilizando https://github.com/mattermost/push-proxy. Para realizar pruebas puedes utilizar https://push-test.mattermost.com, el cual conecta con la ap de ejemplo de Mattermost en iOS publicada en el Apple AppStore. Por favor no utilices este servicio en producción.",
"admin.email.pushServerEx": "Ej.: \"https://push-test.mattermost.com\"",
@@ -1313,4 +1317,4 @@
"web.footer.terms": "Términos",
"web.header.back": "Atrás",
"web.root.singup_info": "Todas las comunicaciones del equipo en un sólo lugar, con búsquedas y accesible desde cualquier parte"
-}
+} \ No newline at end of file
diff --git a/webapp/i18n/pt.json b/webapp/i18n/pt.json
index 0d8e5f4cb..17ffe1b16 100644
--- a/webapp/i18n/pt.json
+++ b/webapp/i18n/pt.json
@@ -1174,7 +1174,7 @@
"user.settings.display.preferTime": "Selecione como você prefere que a hora seja mostrada.",
"user.settings.display.showFullname": "Mostrar primeiro e último nome",
"user.settings.display.showNickname": "Mostras apelidos se um existir, caso contrário mostrar o primeiro e último nome",
- "user.settings.display.showUsername": "Mostrar nome de usuário (equipe padrão)",
+ "user.settings.display.showUsername": "Mostrar nome de usuário (padrão)",
"user.settings.display.teammateDisplay": "Nome de Exibição da Equipe de Trabalho",
"user.settings.display.theme.customTheme": "Tema Customizado",
"user.settings.display.theme.describe": "Abrir para gerenciar seu tema",
@@ -1313,4 +1313,4 @@
"web.footer.terms": "Termos",
"web.header.back": "Voltar",
"web.root.singup_info": "Toda comunicação em um só lugar, pesquisável e acessível em qualquer lugar"
-} \ No newline at end of file
+}
diff --git a/webapp/package.json b/webapp/package.json
index 0d88a6212..25003114e 100644
--- a/webapp/package.json
+++ b/webapp/package.json
@@ -31,7 +31,7 @@
"babel-loader": "6.2.4",
"babel-plugin-transform-runtime": "6.6.0",
"babel-polyfill": "6.7.2",
- "babel-preset-es2015": "6.6.0",
+ "babel-preset-es2015-webpack": "6.4.0",
"babel-preset-react": "6.5.0",
"babel-preset-stage-0": "6.5.0",
"eslint": "2.2.0",
@@ -39,6 +39,7 @@
"exports-loader": "0.6.3",
"extract-text-webpack-plugin": "1.0.1",
"file-loader": "0.8.5",
+ "url-loader": "0.5.7",
"html-loader": "0.4.3",
"copy-webpack-plugin": "1.1.1",
"css-loader": "0.23.1",
@@ -52,6 +53,7 @@
},
"scripts": {
"check": "eslint --ext \".jsx\" --ignore-pattern node_modules --quiet .",
- "build": "webpack --progress"
+ "build": "webpack --optimize-dedupe",
+ "run": "webpack --progress"
}
}
diff --git a/webapp/root.jsx b/webapp/root.jsx
index 8ebe51bd2..2ce220f1d 100644
--- a/webapp/root.jsx
+++ b/webapp/root.jsx
@@ -3,11 +3,11 @@
import $ from 'jquery';
-import 'sass/styles.scss';
import 'bootstrap/dist/css/bootstrap.css';
import 'jasny-bootstrap/dist/css/jasny-bootstrap.css';
import 'bootstrap-colorpicker/dist/css/bootstrap-colorpicker.css';
import 'google-fonts/google-fonts.css';
+import 'sass/styles.scss';
import React from 'react';
import ReactDOM from 'react-dom';
diff --git a/webapp/sass/base/_structure.scss b/webapp/sass/base/_structure.scss
index 56888f8dc..217488334 100644
--- a/webapp/sass/base/_structure.scss
+++ b/webapp/sass/base/_structure.scss
@@ -6,22 +6,24 @@ body {
}
body {
- width: 100%;
- height: 100%;
background: $bg--gray;
+ height: 100%;
position: relative;
+ width: 100%;
+}
- &.sticky {
- background: $bg--white;
+.sticky {
+ background: $white;
- > .container-fluid {
- overflow: auto;
- }
+ > .container-fluid {
+ overflow: auto;
+ }
- .inner-wrap {
- > .row.content {
- min-height: 100%;
+ .inner-wrap {
+ > .row {
+ &.content {
margin-bottom: -89px;
+ min-height: 100%;
}
}
}
@@ -30,9 +32,11 @@ body {
.inner-wrap {
height: 100%;
- > .row.main {
- height: 100%;
- position: relative;
+ > .row {
+ &.main {
+ height: 100%;
+ position: relative;
+ }
}
}
diff --git a/webapp/sass/base/_typography.scss b/webapp/sass/base/_typography.scss
index 676a2c405..f5795d862 100644
--- a/webapp/sass/base/_typography.scss
+++ b/webapp/sass/base/_typography.scss
@@ -6,20 +6,20 @@ strong {
}
a {
+ color: $primary-color;
cursor: pointer;
text-decoration: none;
word-break: break-word;
- color: $color--primary;
-}
-a:focus,
-a:hover {
- color: $color--primary--hover;
+ &:focus,
+ &:hover {
+ color: $primary-color--hover;
+ }
}
body {
+ @include font-smoothing;
font-family: 'Open Sans', sans-serif;
- -webkit-font-smoothing: antialiased;
}
.word-break--all {
diff --git a/webapp/sass/components/_alerts.scss b/webapp/sass/components/_alerts.scss
index 49ca3e8af..cb4c9c9e1 100644
--- a/webapp/sass/components/_alerts.scss
+++ b/webapp/sass/components/_alerts.scss
@@ -1,13 +1,13 @@
@charset 'UTF-8';
.alert {
- padding: 8px 12px;
@include border-radius($border-rad);
+ padding: 8px 12px;
}
.alert--confirm {
display: inline-block;
float: left;
- padding: 4px 10px;
margin: 1px 0 0 10px;
-} \ No newline at end of file
+ padding: 4px 10px;
+}
diff --git a/webapp/sass/components/_buttons.scss b/webapp/sass/components/_buttons.scss
index bb8efbb14..efef11ce9 100644
--- a/webapp/sass/components/_buttons.scss
+++ b/webapp/sass/components/_buttons.scss
@@ -5,19 +5,19 @@
@include border-radius($border-rad);
&.btn-primary {
+ background: $primary-color;
border-color: transparent;
- background: $color--primary;
&:hover,
&:focus,
&:active {
- background: $color--primary--hover;
+ background: $primary-color--hover;
}
}
&.btn-inactive {
+ background: $light-gray;
border-color: transparent;
- background: #707070;
- color: #fff;
+ color: $white;
}
}
diff --git a/webapp/sass/components/_emoticons.scss b/webapp/sass/components/_emoticons.scss
index 661b25d94..80a800863 100644
--- a/webapp/sass/components/_emoticons.scss
+++ b/webapp/sass/components/_emoticons.scss
@@ -1,27 +1,27 @@
@charset 'UTF-8';
.emoticon {
- width: 1.5em;
- height: 1.5em;
+ background-size: contain;
display: inline-block;
+ height: 1.5em;
margin-bottom: .25em;
- background-size: contain;
+ width: 1.5em;
}
.emoticon-suggestion {
@include clearfix;
- width: 100%;
- height: 30px;
cursor: pointer;
font-size: 13px;
+ height: 30px;
line-height: 30px;
+ width: 100%;
}
.emoticon-suggestion__image {
- width: 20px;
height: 20px;
margin: 6px 6px 0 5px;
padding: 0;
text-align: center;
vertical-align: top;
+ width: 20px;
}
diff --git a/webapp/sass/components/_error-bar.scss b/webapp/sass/components/_error-bar.scss
index 2b74a33ae..cda7d25bd 100644
--- a/webapp/sass/components/_error-bar.scss
+++ b/webapp/sass/components/_error-bar.scss
@@ -1,29 +1,28 @@
@charset 'UTF-8';
.error-bar {
- background-color: #09f;
- text-align: center;
- position: relative;
- color: #fff;
+ background-color: darken($primary-color, 5%);
+ color: $white;
+ padding: 5px 30px;
position: absolute;
+ text-align: center;
top: 0;
width: 100%;
z-index: 9999;
- padding: 5px 30px;
&__close {
- position: absolute;
- right: 0;
- top: 0;
- color: #fff;
+ color: $white;
+ font-family: 'Open Sans', sans-serif;
font-size: 20px;
font-weight: 600;
- text-decoration: none;
padding: 0 10px;
- font-family: 'Open Sans', sans-serif;
+ position: absolute;
+ right: 0;
+ text-decoration: none;
+ top: 0;
&:hover {
- color: #fff;
+ color: $white;
text-decoration: none;
}
}
diff --git a/webapp/sass/components/_files.scss b/webapp/sass/components/_files.scss
index b9e2b5f7d..5522c6db8 100644
--- a/webapp/sass/components/_files.scss
+++ b/webapp/sass/components/_files.scss
@@ -1,27 +1,31 @@
@charset 'UTF-8';
.file-preview__container {
- position: relative;
+ height: 100px;
margin: 1px 0 10px;
- width: 100%;
max-height: 100px;
- height: 100px;
- white-space: nowrap;
overflow-x: auto;
overflow-y: hidden;
+ position: relative;
+ white-space: nowrap;
+ width: 100%;
}
.file-preview {
+ @include clearfix;
+ border: 1px solid $light-gray;
display: inline-block;
- width: 120px;
height: 100px;
margin: 0 0 0 10px;
position: relative;
- border: 1px solid #ddd;
- @include clearfix;
+ width: 120px;
- &:hover .file-preview__remove:after {
- @include opacity(1);
+ &:hover {
+ .file-preview__remove {
+ &:after {
+ @include opacity(1);
+ }
+ }
}
&:first-child {
@@ -29,13 +33,13 @@
}
.spinner {
- position: absolute;
- top: 50%;
+ height: 32px;
left: 50%;
margin-left: -16px;
margin-top: -16px;
+ position: absolute;
+ top: 50%;
width: 32px;
- height: 32px;
}
}
@@ -46,14 +50,15 @@
}
.file-preview__remove {
- position: absolute;
- width: 100%;
height: 100%;
left: 0;
+ position: absolute;
top: 0;
+ width: 100%;
&:after {
- background: rgba(0, 0, 0, .4);
+ @include opacity(0);
+ @include alpha-property(background, $black, .4);
content: '';
height: 100%;
left: 0;
@@ -61,33 +66,31 @@
position: absolute;
top: 0;
width: 100%;
- @include opacity(0);
}
i {
- top: 5px;
- right: 5px;
- position: absolute;
- color: #fff;
+ color: $white;
cursor: pointer;
- z-index: 5;
opacity: inherit;
- text-shadow: 0 0px 3px #444;
- text-shadow: 0 0px 3px rgba(0, 0, 0, .7);
+ position: absolute;
+ right: 5px;
+ text-shadow: 0 0 3px alpha-color($black, .7);
+ top: 5px;
+ z-index: 5;
}
}
.image-comment {
background-position: left top;
background-repeat: no-repeat;
- width: 300px;
height: 300px;
+ width: 300px;
}
.file-icon {
- width: 100%;
height: 100%;
+ width: 100%;
&.audio {
@include file-icon('../images/icons/audio.png');
@@ -131,36 +134,30 @@
}
.post-image__column {
- position: relative;
- width: 240px;
- height: 100px;
+ border: 1px solid alpha-color($black, .2);
float: left;
+ height: 100px;
margin: 5px 10px 5px 0;
- border: 1px solid lightgrey;
-
- a {
- text-decoration: none;
- color: grey;
- }
+ position: relative;
+ width: 240px;
}
.post-image__load {
+ background-position: center;
+ background-repeat: no-repeat;
+ background-size: 20px 20px;
height: 100%;
width: 100%;
- background-size: 20px 20px;
- background-repeat: no-repeat;
- background-position: center;
- background-image: url('~images/load.gif');
}
.post-image {
- width: 100%;
- height: 100%;
- background-color: #fff;
+ background-color: $white;
background-repeat: no-repeat;
+ height: 100%;
overflow: hidden;
position: relative;
text-align: center;
+ width: 100%;
&.small {
background-position: center;
@@ -170,60 +167,59 @@
background-position: top left;
}
- .spinner.file__loading {
- position: absolute;
- left: 50%;
- margin-left: -16px;
- top: 50%;
- margin-top: -16px;
+ .spinner {
+ .file__loading {
+ left: 50%;
+ margin-left: -16px;
+ margin-top: -16px;
+ position: absolute;
+ top: 50%;
+ }
}
.file__loaded {
max-width: initial;
+
&.landscape,
&.quadrat {
height: 100px;
}
+
&.portrait {
width: 120px;
}
}
-
- &:hover .file-playback__controls.stop {
- @include opacity(1);
- }
}
.post-image__thumbnail {
+ @include cursor(zoom-in);
float: left;
- width: 50%;
height: 100%;
- cursor: zoom-in;
- cursor: -webkit-zoom-in;
+ width: 50%;
}
.post-image__details {
- float: left;
@include clearfix;
- word-break: break-word;
- width: 50%;
- height: 100%;
- background: white;
- border-left: 1px solid #ddd;
+ background: $white;
+ border-left: 1px solid $light-gray;
+ color: alpha-color($black, .8);
+ float: left;
font-size: 13px;
+ height: 100%;
padding: 7px;
- color: #333;
+ width: 50%;
+ word-break: break-word;
.post-image__name {
- margin-bottom: 3px;
display: block;
+ margin-bottom: 3px;
}
.post-image__download {
+ @include opacity(.7);
+ cursor: pointer;
display: inline-block;
padding-right: 7px;
- cursor: pointer;
- @include opacity(.7);
}
.post-image__type {
@@ -231,57 +227,56 @@
}
.post-image__size {
- margin-left: 4px;
@include opacity(.6);
+ margin-left: 4px;
}
}
.file-details__container {
@include display-flex;
- display: -ms-flexbox;
+ background: $white;
.file-details {
- width: 320px;
height: 270px;
padding: 14px;
text-align: left;
vertical-align: top;
+ width: 320px;
.file-details__name {
- color: #333;
+ color: alpha-color($black, .9);
font-size: 16px;
}
.file-details__info {
- color: grey;
+ color: alpha-color($black, .5);
}
}
+
.file-details__preview {
- width: 320px;
+ border-right: 1px solid $light-gray;
height: 270px;
- border-right: 1px solid #ddd;
vertical-align: center;
+ width: 320px;
// helper to center the image icon in the preview window
.file-details__preview-helper {
- height: 100%;
display: inline-block;
+ height: 100%;
vertical-align: middle;
}
}
}
.file-playback__controls {
- position: absolute;
- right: 5px;
+ @include single-transition(opacity, .6s);
bottom: 0;
- font-size: 22px;
cursor: pointer;
- z-index: 2;
- -webkit-transition: opacity .6s;
- -moz-transition: opacity .6s;
- -o-transition: opacity .6s;
+ font-size: 22px;
+ position: absolute;
+ right: 5px;
transition: opacity .6s;
+ z-index: 2;
&.stop {
@include opacity(0);
@@ -289,6 +284,6 @@
}
.view-image__loading {
- background: black;
+ background: $black;
min-height: 100px;
}
diff --git a/webapp/sass/components/_inputs.scss b/webapp/sass/components/_inputs.scss
index 5e3311182..42ab56128 100644
--- a/webapp/sass/components/_inputs.scss
+++ b/webapp/sass/components/_inputs.scss
@@ -15,12 +15,21 @@
&.no-resize {
resize: none;
}
+
+ &[disabled],
+ [readonly] {
+ @include alpha-property(background, $white, .1);
+ color: inherit;
+ cursor: auto;
+ }
}
-.form-control[disabled],
-.form-control[readonly],
-fieldset[disabled] .form-control {
- cursor: auto;
- background: rgba(#fff, .1);
- color: inherit;
+fieldset {
+ &[disabled] {
+ .form-control {
+ @include alpha-property(background, $white, .1);
+ color: inherit;
+ cursor: auto;
+ }
+ }
}
diff --git a/webapp/sass/components/_links.scss b/webapp/sass/components/_links.scss
index f31008b4f..3d7472670 100644
--- a/webapp/sass/components/_links.scss
+++ b/webapp/sass/components/_links.scss
@@ -1,23 +1,22 @@
@charset 'UTF-8';
a {
+ color: $primary-color;
cursor: pointer;
text-decoration: none;
word-break: break-word;
- color: $color--primary;
-}
-a:focus,
-a:hover {
- color: $color--primary--hover;
+ &:hover,
+ &:focus {
+ color: $primary-color--hover;
+ }
}
-.text-danger,
-a.text-danger {
- color: #e05f5d;
+.text-danger {
+ color: desaturate($red, 20%);
&:hover,
&:focus {
- color: #e05f5d;
+ color: desaturate($red, 20%);
}
}
diff --git a/webapp/sass/components/_mentions.scss b/webapp/sass/components/_mentions.scss
index 98efc599d..98ae7d320 100644
--- a/webapp/sass/components/_mentions.scss
+++ b/webapp/sass/components/_mentions.scss
@@ -1,24 +1,24 @@
@charset 'UTF-8';
.mention {
- color: #fff;
- background: $color--primary;
+ @include border-radius(3px);
+ background: $primary-color;
+ color: $white;
+ padding-bottom: 2px;
position: relative;
z-index: 10;
- padding-bottom: 2px;
- @include border-radius(3px);
}
.mentions__name {
- position: relative;
- width: 100%;
+ cursor: pointer;
+ font-size: 13px;
height: 36px;
- padding: 2px;
- z-index: 101;
line-height: 36px;
- font-size: 13px;
- cursor: pointer;
+ padding: 2px;
+ position: relative;
white-space: nowrap;
+ width: 100%;
+ z-index: 101;
.mention-align {
@include clearfix;
@@ -28,29 +28,29 @@
}
.mention__image {
- margin-right: 6px;
- height: 32px;
- width: 32px;
- line-height: 36px;
+ @include border-radius(32px);
display: block;
font-size: 20px;
+ height: 32px;
+ line-height: 36px;
+ margin-right: 6px;
text-align: center;
- @include border-radius(32px);
+ width: 32px;
.mention--align {
+ display: inline-block;
max-width: 80%;
overflow: hidden;
- display: inline-block;
- white-space: nowrap;
text-overflow: ellipsis;
+ white-space: nowrap;
}
}
.mention__fullname {
- color: grey;
+ color: $dark-gray;
padding-left: 10px;
}
.mention--highlight {
- background-color: #fff2bb;
+ background-color: $yellow;
}
diff --git a/webapp/sass/components/_modal.scss b/webapp/sass/components/_modal.scss
index a8e8a99f7..94378aabe 100644
--- a/webapp/sass/components/_modal.scss
+++ b/webapp/sass/components/_modal.scss
@@ -1,12 +1,24 @@
@charset 'UTF-8';
-#channel_members_modal .modal-body {
- min-height: 110px;
+.browser--ie {
+ .modal {
+ .modal-dialog {
+ @include translateY(0);
+ }
+ }
}
.modal-body {
- padding: 20px 15px;
overflow: auto;
+ padding: 20px 15px;
+
+ .edit-modal-body {
+ overflow: visible;
+
+ .suggestion-list__content {
+ max-height: 150px;
+ }
+ }
}
.more-table {
@@ -15,27 +27,23 @@
}
.modal {
+ color: alpha-color($black, .9);
width: 100%;
- color: #333;
- body.browser--IE & {
- .modal-dialog {
- @include translateY(0);
- }
- }
-
- &.image_modal {
- .modal-backdrop.in {
- @include opacity(.7);
+ &.modal-image {
+ .modal-backdrop {
+ &.in {
+ @include opacity(.7);
+ }
}
}
.custom-textarea {
+ border-color: $light-gray;
color: inherit;
- border-color: #ccc;
&:focus {
- border-color: #ccc;
+ border-color: $light-gray;
box-shadow: none;
}
}
@@ -44,15 +52,15 @@
font-size: 13px;
&.btn-default {
- border: none;
background: transparent;
+ border: none;
}
}
.info__label {
font-weight: 600;
- text-align: right;
padding-right: 0;
+ text-align: right;
}
.team-member-list {
@@ -60,21 +68,21 @@
}
.remove__member {
+ color: $dark-gray;
float: right;
- color: #999;
font-size: 20px;
line-height: 0;
padding: 6px;
&:hover {
- color: #e56565;
+ color: $red;
}
}
.modal-dialog {
- max-width: 95%;
margin-left: auto;
margin-right: auto;
+ max-width: 95%;
}
.modal-push-down {
@@ -82,51 +90,51 @@
}
.modal-next-bar {
+ height: 100%;
position: absolute;
- top: 0px;
right: 0;
- height: 100%;
+ top: 0;
}
.modal-header {
- border-radius: 0;
- background: $color--primary;
- color: #fff;
- padding: 15px 15px 11px;
- border: 1px solid #ddd;
- min-height: 56px;
+ @include border-radius(0);
@include clearfix;
+ background: $primary-color;
+ border: 1px solid $light-gray;
+ color: $white;
+ min-height: 56px;
+ padding: 15px 15px 11px;
.modal-title {
+ color: $bg--gray;
float: left;
font-size: 17px;
line-height: 27px;
- color: #f4f4f4;
.name {
+ color: $white;
font-weight: 600;
- color: #fff;
}
}
.modal-action {
- padding: 0;
margin: 0;
+ padding: 0;
}
- button.close {
- color: #fff;
+ .close {
@include opacity(1);
- z-index: 5;
- width: 30px;
+ @include single-transition(all, .25s, ease-in);
+ color: $white;
height: 30px;
line-height: 30px;
- @include single-transition(all, .25s, ease-in);
position: absolute;
right: 10px;
+ width: 30px;
+ z-index: 5;
&:hover {
- background: rgba(0, 0, 0, .1);
+ @include alpha-property(background, $black, .1);
}
span {
@@ -149,8 +157,8 @@
}
.no-channel-message {
- text-align: center;
padding: 2em 1em;
+ text-align: center;
.primary-message {
font-size: 1.25em;
@@ -168,46 +176,46 @@
}
.modal-chevron-icon {
- top: 50%;
font-size: 120%;
+ top: 50%;
}
.modal-prev-bar {
- position: absolute;
- top: 0px;
- left: 0;
height: 100%;
+ left: 0;
+ position: absolute;
+ top: 0;
}
- &#more_channels {
+ &.more-channel__modal {
.modal-body {
padding: 0;
}
}
.modal-image {
- position: relative;
- width: 100%;
height: 100%;
margin: 0 auto;
max-width: 100%;
+ position: relative;
+ width: 100%;
.modal-body {
@include clearfix;
- height: 100%;
display: table;
+ height: 100%;
+ max-height: 100%;
table-layout: fixed;
width: 100%;
- max-height: 100%;
}
- .image-wrapper {
- position: relative;
- max-width: 90%;
+ .modal-image__wrapper {
@include border-radius(3px);
display: table-cell;
- vertical-align: middle;
+ max-width: 90%;
+ position: relative;
text-align: center;
+ vertical-align: middle;
width: 100%;
&:hover {
@@ -215,33 +223,31 @@
}
&.default {
- width: 100%;
height: 80%;
+ width: 100%;
}
audio,
canvas,
progress,
video {
- max-width: 100%;
- height: auto;
display: block;
+ height: auto;
+ max-width: 100%;
}
.modal-close {
- background: url('../images/close.png') no-repeat;
+ @include single-transition(opacity, .6s);
@include background-size(100% 100%);
- width: 37px;
+ @include opacity(0);
+ background-image: url('../images/close.png');
+ cursor: pointer;
height: 37px;
position: absolute;
right: -13px;
top: -13px;
- @include opacity(0);
- -webkit-transition: opacity .6s;
- -moz-transition: opacity .6s;
- -o-transition: opacity .6s;
transition: opacity .6s;
- cursor: pointer;
+ width: 37px;
z-index: 9999;
&.modal-close--show {
@@ -250,99 +256,109 @@
}
> div {
+ display: inline-block;
min-height: 100px;
min-width: 320px;
- background: #fff;
- display: inline-block;
position: relative;
+ }
- &:hover .file-playback__controls.stop {
- @include opacity(1);
- }
+ code {
+ min-height: 130px;
+ min-width: 330px;
+ }
+
+ pre, code {
+ display: inline-block;
+ }
+
+ .post-body--code {
+ max-height: calc(100vh - 80px);
+ max-width: calc(100vw - 80px);
+ overflow: auto;
}
img {
- max-width: 100%;
max-height: 100%;
+ max-width: 100%;
}
- .spinner.file__loading {
- z-index: 2;
- position: absolute;
- left: 50%;
- margin-left: -16px;
- top: 50%;
- margin-top: -16px;
+ .spinner {
+ .file__loading {
+ left: 50%;
+ margin-left: -16px;
+ margin-top: -16px;
+ position: absolute;
+ top: 50%;
+ z-index: 2;
+ }
}
}
.modal-content {
+ background: alpha-color($black, 0);
+ border: none;
box-shadow: none;
- background: rgba(0, 0, 0, 0);
- width: 100%;
height: 100%;
padding: 0;
- border: none;
+ width: 100%;
}
- .image-body {
- vertical-align: middle;
+ .modal-image__body {
display: table-cell;
- text-align: center;
height: 100%;
+ overflow: visible;
padding: 0;
position: relative;
- overflow: visible;
+ text-align: center;
+ vertical-align: middle;
}
.image-control {
- width: 50px;
- height: 45px;
+ background: url('../images/prev.png') left no-repeat;
float: left;
- background: url(../images/prev.png) left no-repeat;
- top: 50%;
- position: relative;
+ height: 45px;
margin-top: -23px;
+ position: relative;
+ top: 50%;
+ width: 50px;
+
&.image-next {
+ background: url('../images/next.png') left no-repeat;
float: right;
- background: url(../images/next.png) left no-repeat;
}
}
.loader-image {
- position: absolute;
- top: 0;
bottom: 0;
left: 0;
- right: 0;
margin: auto;
+ position: absolute;
+ right: 0;
+ top: 0;
}
.loader-percent {
- position: absolute;
- top: 55px;
bottom: 0;
+ color: $dark-gray;
+ height: 20px;
left: 0;
- right: 0;
margin: auto;
- color: grey;
- height: 20px;
+ position: absolute;
+ right: 0;
+ top: 55px;
}
.modal-button-bar {
- position: absolute;
- bottom: -40px;
- left: 0px;
- right: 0px;
- background-color: #222;
+ @include single-transition(opacity, .6s);
@include border-radius(0 0 3px 3px);
@include opacity(0);
- -webkit-transition: opacity .6s;
- -moz-transition: opacity .6s;
- -o-transition: opacity .6s;
- transition: opacity .6s;
+ background-color: $black;
+ bottom: -40px;
+ left: 0;
line-height: 40px;
padding: 0 10px;
+ position: absolute;
+ right: 0;
&.footer--show {
@include opacity(1);
@@ -356,10 +372,10 @@
}
.text {
- vertical-align: middle;
bottom: 0;
- color: white;
+ color: $white;
margin-left: 5px;
+ vertical-align: middle;
}
.public-link {
@@ -376,11 +392,12 @@
}
.row--invite {
- margin-right: 40px;
@include clearfix;
+ margin-right: 40px;
.col-sm-6 {
padding: 0 0 0 15px;
+
&:first-child {
padding-left: 0;
}
@@ -389,26 +406,26 @@
.more-modal {
.user-list {
- overflow-y: auto;
- overflow-x: hidden;
margin-top: 10px;
+ overflow-x: hidden;
+ overflow-y: auto;
position: relative;
}
.modal-body {
- padding: 10px 0 20px;
overflow-x: hidden;
+ padding: 10px 0 20px;
}
.filter-row {
- margin: 10px 0;
@include clearfix;
+ margin: 10px 0;
}
.member-count {
- margin-top: 5px;
- float: right;
@include opacity(.8);
+ float: right;
+ margin-top: 5px;
}
.more-purpose {
@@ -416,21 +433,13 @@
}
}
-.modal-body.edit-modal-body {
- overflow: visible;
-
- .suggestion-list__content {
- max-height: 150px;
- }
-}
-
.more-modal__list {
- overflow: auto;
display: flex;
flex-direction: column;
+ overflow: auto;
.popover & {
- font-size: 0.95em;
+ font-size: .95em;
.more-modal__row {
padding: 5px 10px;
@@ -442,9 +451,9 @@
}
.more-modal__image {
+ @include border-radius(60px);
flex-grow: 0;
flex-shrink: 0;
- @include border-radius(60px);
margin-right: 8px;
}
@@ -462,33 +471,33 @@
}
.more-modal__name {
- font-weight: 600;
font-size: .95em;
- white-space: nowrap;
+ font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
+ white-space: nowrap;
}
.more-modal__description {
@include opacity(.7);
display: block;
- white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
+ white-space: nowrap;
}
.more-modal__row {
+ border-bottom: 1px solid;
display: flex;
padding: 8px 15px;
- border-bottom: 1px solid;
}
p {
+ @include opacity(.8);
font-size: .9em;
+ margin: 5px 0;
overflow: hidden;
text-overflow: ellipsis;
- @include opacity(.8);
- margin: 5px 0;
}
}
diff --git a/webapp/sass/components/_oauth.scss b/webapp/sass/components/_oauth.scss
index cd8382a5c..04840457c 100644
--- a/webapp/sass/components/_oauth.scss
+++ b/webapp/sass/components/_oauth.scss
@@ -1,18 +1,18 @@
@charset 'UTF-8';
.prompt {
- background: #fff;
- border: 1px solid #ddd;
- padding: 1em 2em 0;
+ background: $white;
+ border: 1px solid $light-gray;
margin: 50px auto;
max-width: 90%;
+ padding: 1em 2em 0;
width: 600px;
.prompt__heading {
+ display: table;
font-size: em(20px);
line-height: normal;
margin: 1em 0;
- display: table;
width: 100%;
> div {
@@ -26,13 +26,13 @@
}
.prompt__allow {
- margin: 1em 0;
font-size: em(24px);
+ margin: 1em 0;
}
.prompt__buttons {
- text-align: right;
- border-top: 1px solid #ddd;
+ border-top: 1px solid $dark-gray;
padding: 1.5em 0;
+ text-align: right;
}
}
diff --git a/webapp/sass/components/_popover.scss b/webapp/sass/components/_popover.scss
index de66b67a7..0b2769f77 100644
--- a/webapp/sass/components/_popover.scss
+++ b/webapp/sass/components/_popover.scss
@@ -3,32 +3,38 @@
.popover {
@include border-radius($border-rad);
- color: #333;
+ color: lighten($black, 25%);
&.bottom,
&.right,
&.top,
&.left {
- > .arrow:after {
- border-color: transparent;
+ > .arrow {
+ &:after {
+ border-color: transparent;
+ }
}
}
.popover-title {
- background: rgba(black, .05);
+ background: alpha-color($black, .05);
padding: 8px 10px;
}
.popover-content {
- p:last-child {
- margin-bottom: 5px;
+ p {
+ &:last-child {
+ margin-bottom: 5px;
+ }
}
}
}
-.channel-header__info .popover-content {
- max-height: 250px;
- overflow: auto;
+.channel-header__info {
+ .popover-content {
+ max-height: 250px;
+ overflow: auto;
+ }
}
.user-popover {
@@ -36,32 +42,35 @@
display: inline-block;
}
-.code-popover .popover-content {
- padding: 5px;
+.code-popover {
+ .popover-content {
+ padding: 5px;
+ }
}
.user-popover__image {
- margin: 0 0 10px;
@include border-radius(128px);
+ margin: 0 0 10px;
}
.user-popover__email {
+ display: block;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
- display: block;
}
.search-help-popover {
- visibility: hidden;
- max-width: none;
- width: 100%;
- top: 36px;
@include single-transition(opacity, .3s, ease-in);
font-size: em(13px);
+ max-width: none;
+ top: 36px;
+ visibility: hidden;
+ width: 100%;
&.autocomplete {
display: block;
+
.popover-content {
padding: 10px;
position: relative;
@@ -69,65 +78,68 @@
}
.search-autocomplete__divider {
- margin: 10px 0 5px;
line-height: 21px;
+ margin: 10px 0 5px;
position: relative;
+
&:first-child {
margin-top: 5px;
}
- span {
+
+ > span {
+ background: $white;
display: inline-block;
padding-right: 10px;
- background: #fff;
- z-index: 5;
position: relative;
+ z-index: 5;
}
+
&:before {
+ @include opacity(.2);
+ background: $dark-gray;
content: '';
- position: absolute;
- width: 100%;
height: 1px;
- background: #ddd;
- top: 10px;
left: 0;
- @include opacity(.2);
+ position: absolute;
+ top: 10px;
+ width: 100%;
}
}
.search-autocomplete__item {
+ @include border-radius(2px);
cursor: pointer;
- padding: 6px 8px;
margin: 3px 0 0 5px;
- @include border-radius(2px);
- white-space: nowrap;
overflow: hidden;
+ padding: 6px 8px;
text-overflow: ellipsis;
+ white-space: nowrap;
&:hover {
- background: rgba(black, .1);
+ background: alpha-color($black, .1);
}
&.selected {
- background: rgba(black, .2);
+ background: alpha-color($black, .2);
}
.fa {
- margin-right: 5px;
@include opacity(.5);
+ margin-right: 5px;
}
.profile-img {
- margin-top: -1px;
height: 16px;
margin-right: 6px;
+ margin-top: -1px;
width: 16px;
}
}
&.bottom > .arrow {
- top: -18px;
border-width: 9px;
left: 30px;
+ top: -18px;
}
.popover-content {
@@ -142,9 +154,11 @@
ul {
padding-left: 17px;
+
span {
@include opacity(.8);
}
+
strong,
b {
@include opacity(1);
@@ -156,25 +170,28 @@
}
&.visible {
- visibility: visible;
@include opacity(1);
+ visibility: visible;
}
}
-#member-list-popover {
+.member-list__popover {
max-width: initial;
+
.popover-content {
- position: relative;
+ max-height: 350px;
padding: 0;
+ position: relative;
width: 260px;
- max-height: 350px;
+
.text-nowrap {
+ font-size: 13px;
+ line-height: 26px;
+ overflow: hidden;
padding: 6px 10px;
width: 100%;
- overflow: hidden;
- line-height: 26px;
- font-size: 13px;
}
+
.more__name {
margin-left: 6px;
max-width: 140px;
diff --git a/webapp/sass/components/_scrollbar.scss b/webapp/sass/components/_scrollbar.scss
index 4ecb38b1f..0bb0e7e21 100644
--- a/webapp/sass/components/_scrollbar.scss
+++ b/webapp/sass/components/_scrollbar.scss
@@ -1,28 +1,25 @@
@charset 'UTF-8';
-::-webkit-scrollbar
-{
- width: 8px; /* for vertical scrollbars */
- height: 8px; /* for horizontal scrollbars */
+::-webkit-scrollbar {
+ height: 8px; // for horizontal scrollbars
+ width: 8px; // for vertical scrollbars
}
-::-webkit-scrollbar-track
-{
- background: rgba(0, 0, 0, 0.1);
+::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, .1);
}
-::-webkit-scrollbar-thumb
-{
+::-webkit-scrollbar-thumb {
@include border-radius($border-rad * 2);
- background: rgba(0, 0, 0, 0.4);
+ background: rgba(0, 0, 0, .4);
}
-body{
- scrollbar-face-color: rgba(0, 0, 0, 0.1);
- scrollbar-shadow-color: #2D2C4D;
- scrollbar-highlight-color:#7D7E94;
+body {
scrollbar-3dlight-color: #7D7E94;
- scrollbar-darkshadow-color: #2D2C4D;
- scrollbar-track-color: rgba(0, 0, 0, 0.1);
scrollbar-arrow-color: #C1C1D1;
+ scrollbar-darkshadow-color: #2D2C4D;
+ scrollbar-face-color: rgba(0, 0, 0, .1);
+ scrollbar-highlight-color: #7D7E94;
+ scrollbar-shadow-color: #2D2C4D;
+ scrollbar-track-color: rgba(0, 0, 0, .1);
} \ No newline at end of file
diff --git a/webapp/sass/components/_search.scss b/webapp/sass/components/_search.scss
index faf0b8177..499c4fad4 100644
--- a/webapp/sass/components/_search.scss
+++ b/webapp/sass/components/_search.scss
@@ -1,15 +1,19 @@
@charset 'UTF-8';
-#channel-header .search-bar__container {
- padding: 0 9px 0 3px;
+.channel-header {
+ .search-bar__container {
+ padding: 0 9px 0 3px;
+ }
}
.search-bar__container {
- padding: 12px 8px 0 0;
@include flex(0 0 56px);
+ padding: 12px 8px 0 0;
- .sidebar--right.move--left & {
- padding-right: 42px;
+ .sidebar--right {
+ &.move--left & {
+ padding-right: 42px;
+ }
}
}
@@ -18,62 +22,60 @@
}
.search__clear {
+ @include single-transition(all, .2s, linear);
+ @include translateX(60px);
+ cursor: pointer;
display: none;
- position: absolute;
- right: 0;
line-height: 45px;
margin-right: 13px;
- @include single-transition(all, .2s, linear);
- @include translateX(60px);
+ position: absolute;
+ right: 0;
z-index: 5;
- cursor: pointer;
}
.search-item-snippet {
@include clearfix;
text-overflow: ellipsis;
+
+ ul,
+ ol {
+ padding-left: 20px;
+ }
}
.sidebar__collapse {
+ @include single-transition(all, .2s, linear);
+ @include translateX(0);
cursor: pointer;
- z-index: 5;
- fill: #fff;
- position: absolute;
- left: 0;
+ display: none;
+ fill: $white;
font-size: 35px;
- width: 49px;
- @include single-transition(all, .2s, linear);
- @include translateX(0px);
- text-align: center;
- padding-left: 1px;
+ left: 0;
line-height: 40px;
- display: none;
-}
-
-.search-item-snippet {
- ul,
- ol {
- padding-left: 20px;
- }
+ padding-left: 1px;
+ position: absolute;
+ text-align: center;
+ width: 49px;
+ z-index: 5;
}
.sidebar__search-icon {
- position: absolute;
- top: 15px;
- margin-left: 10px;
- font-size: 14px;
@include opacity(.5);
+ color: $dark-gray;
display: none;
- color: #777;
+ font-size: 14px;
+ margin-left: 10px;
+ position: absolute;
+ top: 15px;
}
.search__form {
position: relative;
.search-bar__container & {
- margin: 0;
- border: 1px solid #ddd;
@include border-radius(2px);
+ border: 1px solid $light-gray;
+ margin: 0;
width: 300px;
}
@@ -82,21 +84,21 @@
}
.search-bar {
+ box-shadow: none;
height: 40px;
padding-right: 30px;
- box-shadow: none;
.search-bar__container & {
- height: 30px;
border: none;
+ height: 30px;
}
}
.glyphicon-refresh-animate {
- top: 27px;
+ color: $dark-gray;
position: absolute;
right: 27px;
- color: #999;
+ top: 27px;
.search-bar__container & {
right: 7px;
@@ -106,30 +108,29 @@
}
.search-items-container {
- position: relative;
- overflow: auto;
- -webkit-overflow-scrolling: touch;
@include flex(1 1 auto);
+ -webkit-overflow-scrolling: touch;
height: calc(100% - 56px);
+ overflow: auto;
padding-top: 10px;
+ position: relative;
}
.search-results-header {
+ border-bottom: $border-gray;
+ color: #999999;
font-size: 1em;
- text-transform: uppercase;
- color: #999;
font-weight: 400;
- color: #888;
height: 44px;
line-height: 44px;
- padding: 0 10px 0 10px;
- border-bottom: $border-gray;
+ padding: 0 10px;
+ text-transform: uppercase;
}
.search-item__container {
.post {
- padding: 0 1em 1em;
margin: 0;
+ padding: 0 1em 1em;
&:first-child {
border: none;
@@ -137,17 +138,17 @@
.search-channel__name {
font-weight: 600;
- margin: 0 0 10px 0;
+ margin: 0 0 10px;
}
}
}
.search-item__jump {
+ @include opacity(.8);
+ font-size: 13px;
position: absolute;
right: 0;
- top: 0px;
- font-size: 13px;
- @include opacity(.8);
+ top: 0;
&:hover {
@include opacity(1);
@@ -155,9 +156,9 @@
}
.search-item__comment {
+ margin-right: 35px;
position: absolute;
right: 0;
- margin-right: 35px;
top: 0;
}
@@ -171,5 +172,5 @@
}
.search-highlight {
- background-color: #fff2bb;
+ background-color: $yellow;
}
diff --git a/webapp/sass/components/_suggestion-list.scss b/webapp/sass/components/_suggestion-list.scss
index 125a3cf32..0100026ca 100644
--- a/webapp/sass/components/_suggestion-list.scss
+++ b/webapp/sass/components/_suggestion-list.scss
@@ -1,9 +1,9 @@
@charset 'UTF-8';
.suggestion-list {
+ @extend %popover-box-shadow;
width: 100%;
z-index: 100;
- @extend %popover-box-shadow;
}
.suggestion-list--top {
@@ -11,31 +11,31 @@
}
.suggestion-list__content {
- width: 100%;
- max-height: 292px;
- background-color: #fff;
+ background-color: $white;
border: $border-gray;
+ max-height: 292px;
overflow-x: hidden;
overflow-y: scroll;
+ width: 100%;
.command {
- position: relative;
- width: 100%;
+ border-bottom: 1px solid $light-gray;
+ font-size: .95em;
line-height: 24px;
padding: 5px 10px 8px;
+ position: relative;
+ width: 100%;
z-index: 101;
- font-size: .95em;
- border-bottom: 1px solid #ddd;
.command__desc {
- margin-left: 5px;
@include opacity(.5);
line-height: normal;
+ margin-left: 5px;
}
}
}
.suggestion-list__content--top {
- position: absolute;
bottom: 0;
+ position: absolute;
}
diff --git a/webapp/sass/components/_tooltip.scss b/webapp/sass/components/_tooltip.scss
index 44c10edb9..5e71e3a7b 100644
--- a/webapp/sass/components/_tooltip.scss
+++ b/webapp/sass/components/_tooltip.scss
@@ -2,9 +2,9 @@
.tooltip {
.tooltip-inner {
- word-break: break-word;
font-size: 13px;
- padding: 3px 10px 4px;
font-weight: 500;
+ padding: 3px 10px 4px;
+ word-break: break-word;
}
}
diff --git a/webapp/sass/components/_tutorial.scss b/webapp/sass/components/_tutorial.scss
index f15919009..6545d7764 100644
--- a/webapp/sass/components/_tutorial.scss
+++ b/webapp/sass/components/_tutorial.scss
@@ -1,72 +1,76 @@
@charset 'UTF-8';
.tip-backdrop {
- background: rgba(black, .5);
+ background: alpha-color($black, .5);
+ height: 100%;
+ left: 0;
position: absolute;
top: 0;
- left: 0;
width: 100%;
- height: 100%;
z-index: 999;
}
.tip-overlay {
- width: 350px;
- max-width: 90%;
- position: absolute;
- background-color: #fff;
@include border-radius(3px);
+ background-color: $white;
+ max-width: 90%;
padding: 20px;
+ position: absolute;
+ width: 350px;
z-index: 1000;
.arrow {
+ border-color: transparent;
+ border-style: solid;
border-width: 10px;
- position: absolute;
display: block;
- width: 0;
height: 0;
- border-color: transparent;
- border-style: solid;
+ position: absolute;
+ width: 0;
}
&.tip-overlay--sidebar {
- max-width: 75%;
margin: 50px 0 0 10px;
+ max-width: 75%;
min-height: 330px;
+
.tutorial__footer {
- position: absolute;
- width: 100%;
bottom: 20px;
left: 0;
padding: 0 20px;
+ position: absolute;
+ width: 100%;
}
+
.arrow {
- top: 80px;
+ border-left-width: 0;
+ border-right-color: $white;
left: -10px;
margin-top: -10px;
- border-left-width: 0;
- border-right-color: #fff;
+ top: 80px;
}
}
&.tip-overlay--header {
margin: 10px 0 0 10px;
+
.arrow {
- top: 15px;
- left: -10px;
border-left-width: 0;
- border-right-color: #fff;
+ border-right-color: $white;
+ left: -10px;
+ top: 15px;
}
}
&.tip-overlay--chat {
margin-top: -10px;
+
.arrow {
- left: 50%;
- margin-left: -10px;
border-bottom-width: 0;
- border-top-color: #fff;
+ border-top-color: $white;
bottom: -10px;
+ left: 50%;
+ margin-left: -10px;
}
}
@@ -86,18 +90,18 @@
}
.btn {
- background: #ccc;
- color: #fff;
@include border-radius(3px);
+ background: alpha-color($black, .3);
border: none;
+ color: $white;
margin: 10px 0;
&:hover,
&:active,
&:focus {
- color: #fff;
+ background: alpha-color($black, .4);
border: none;
- background: #bbb;
+ color: $white;
}
}
@@ -110,81 +114,88 @@
}
.tutorial__circles {
- margin: 1.5em 0px -1.7em -4px;
+ margin: 1.5em 0 -1.7em -4px;
.circle {
- width: 7px;
height: 7px;
margin: 0 4px;
+ width: 7px;
}
}
}
.tip-button {
- z-index: 998;
+ cursor: pointer;
+ position: relative;
right: -10px;
top: -10px;
- position: relative;
- cursor: pointer;
+ z-index: 998;
}
.tip-div {
position: absolute;
- top: 0px;
- right: 0px;
+ right: 0;
+ top: 0;
&.tip-overlay--header {
- top: 21px;
right: 2px;
+ top: 21px;
+
.tip-button {
@include opacity(.8);
}
}
&.tip-overlay--sidebar {
- left: 0;
@include opacity(.8);
+ left: 0;
top: -9px;
}
}
.tutorial-steps__container {
- text-align: center;
- width: 100%;
display: table;
height: 100%;
+ text-align: center;
+ width: 100%;
+
.tutorial__content {
display: table-cell;
- vertical-align: middle;
- padding-bottom: 100px;
padding: 20px 40px 40px;
+ vertical-align: middle;
+
.tutorial__steps {
- position: relative;
+ display: inline-block;
+ margin-bottom: 50px;
max-width: 310px;
min-height: 370px;
- margin-bottom: 50px;
+ position: relative;
text-align: left;
- display: inline-block;
}
}
+
.tutorial__footer {
- position: absolute;
bottom: 0;
+ position: absolute;
}
+
h1 {
font-size: em(40px);
- margin: -20px 0 30px;
font-weight: 600;
+ margin: -20px 0 30px;
}
+
h3 {
font-size: em(24px);
- margin-bottom: 30px;
font-weight: 600;
+ margin-bottom: 30px;
}
+
.tutorial__circles {
- position: absolute;
bottom: 40px;
+ position: absolute;
}
+
.tutorial-skip {
margin-left: 13px;
}
@@ -192,17 +203,19 @@
.tutorial__circles {
margin: 2em 0;
+
.circle {
- width: 9px;
- height: 9px;
@include border-radius(9px);
@include opacity(.2);
- background: #000;
+ background: $black;
display: inline-block;
+ height: 9px;
margin: 0 5px;
+ width: 9px;
+
&.active {
- background: $color--primary;
@include opacity(1);
+ background: $primary-color;
}
}
}
diff --git a/webapp/sass/components/_videos.scss b/webapp/sass/components/_videos.scss
index 91db750bd..e009e6538 100644
--- a/webapp/sass/components/_videos.scss
+++ b/webapp/sass/components/_videos.scss
@@ -1,65 +1,68 @@
@charset 'UTF-8';
.video-div {
- position: relative;
- max-width: 480px;
margin-bottom: 8px;
+ max-width: 480px;
+ position: relative;
+
.video-thumbnail {
- max-width: 100%;
height: auto;
+ max-width: 100%;
}
+
.block {
- background-color: rgba(0,0,0,.5);
+ background-color: alpha-color($black, .5);
border-radius: 10px;
+ height: 150px;
+ left: 50%;
+ margin: -75px 0 0 -100px;
position: absolute;
top: 50%;
- left: 50%;
- margin-top: -100px;
width: 200px;
- margin: -75px 0 0 -100px;
- height: 150px;
}
}
.video-type {
@include opacity(.8);
font-size: 15px;
- margin: 0px;
- padding: 0px;
+ margin: 0;
+ padding: 0;
}
.video-title {
font-size: 15px;
margin-top: 3px;
}
+
.play-button {
- width: 100px;
+ @include border-radius(14px);
+ border: 4px solid alpha-color($white, .4);
height: 100px;
position: absolute;
- top: 26px;
right: 51px;
- border: 4px solid;
- border-color: rgba(255,255,255,.4);
- border-radius: 14px;
-}
-.play-button span {
- position: absolute;
- top: 10px;
- left: 20px;
- width: 0;
- height: 0;
- border-top: 36px solid transparent;
- border-bottom: 36px solid transparent;
- border-left: 60px solid rgba(255,255,255,.4);
+ top: 26px;
+ width: 100px;
+
+ span {
+ border-bottom: 36px solid transparent;
+ border-left: 60px solid alpha-color($white, .4);
+ border-top: 36px solid transparent;
+ height: 0;
+ left: 20px;
+ position: absolute;
+ top: 10px;
+ width: 0;
+ }
}
.img-div {
-moz-force-broken-image-icon: 1;
- position: relative;
- max-width: 450px;
- max-height: 500px;
- margin-bottom: 8px;
border-radius: 5px;
+ margin-bottom: 8px;
+ max-height: 500px;
+ max-width: 450px;
+ position: relative;
+
&.placeholder {
height: 500px;
}
diff --git a/webapp/sass/layout/_content.scss b/webapp/sass/layout/_content.scss
index 71bef0d7f..bbc09a80e 100644
--- a/webapp/sass/layout/_content.scss
+++ b/webapp/sass/layout/_content.scss
@@ -15,21 +15,22 @@
}
.app__content {
+ @include display-flex;
+ @include flex-direction(column);
+ background: $white;
height: 100%;
- padding-top: 50px;
margin-left: 220px;
+ padding-top: 50px;
position: relative;
- background: #fff;
- @include display-flex;
- @include flex-direction(column);
.channel__wrap & {
padding-top: 0;
}
}
+
#post-create {
@include flex(0 0 auto);
- background: #fff;
+ background: $white;
width: 100%;
z-index: 3;
}
@@ -37,12 +38,12 @@
#archive-link-home {
@include flex(0 0 auto);
cursor: pointer;
- padding: 10px 20px;
font-size: 13px;
+ padding: 10px 20px;
.fa {
- font-size: 11px;
@include opacity(.7);
+ font-size: 11px;
}
a {
@@ -50,36 +51,6 @@
}
}
-.post-list {
- .new-messages-hr {
- margin-top: 5px;
- margin-bottom: 0px;
- border: 0;
- border-top: 1px solid #f80;
- }
-
- .new-messages-text {
- margin-top: 2px;
- margin-bottom: 5px;
- color: #f80;
- text-align: center;
- }
-}
-
-.new-messages-hr {
- margin-top: 5px;
- margin-bottom: 0px;
- border: 0;
- border-top: 1px solid #f80;
-}
-
-.new-messages-text {
- margin-top: 2px;
- margin-bottom: 5px;
- color: #f80;
- text-align: center;
-}
-
.delete-message-text {
margin-top: 10px;
}
diff --git a/webapp/sass/layout/_footer.scss b/webapp/sass/layout/_footer.scss
index 5624e6376..36f1fbc70 100644
--- a/webapp/sass/layout/_footer.scss
+++ b/webapp/sass/layout/_footer.scss
@@ -1,22 +1,22 @@
@charset 'UTF-8';
.footer-pane {
- background: #eee;
+ background: $light-gray;
padding-bottom: 1em;
.footer-link {
padding: 0 1.5em;
&.copyright {
- color: #999;
+ color: $dark-gray;
padding-right: 0;
}
}
.footer-site-name {
- padding: 1.5em 0 1em;
font-size: 14px;
font-weight: bold;
+ padding: 1.5em 0 1em;
text-transform: uppercase;
}
}
diff --git a/webapp/sass/layout/_forms.scss b/webapp/sass/layout/_forms.scss
index 1f51603cc..259beeb57 100644
--- a/webapp/sass/layout/_forms.scss
+++ b/webapp/sass/layout/_forms.scss
@@ -6,32 +6,32 @@
}
.form__label {
- text-align: left;
- padding-right: 3px;
- font-weight: 600;
font-size: 1.1em;
+ font-weight: 600;
+ padding-right: 3px;
+ text-align: left;
&.light {
- font-weight: normal;
- color: #999;
+ color: $dark-gray;
font-size: 1.05em;
font-style: italic;
+ font-weight: normal;
padding-top: 2px;
}
}
.input__help {
+ @include opacity(.8);
color: inherit;
margin: 10px 0 0 10px;
word-break: break-word;
- @include opacity(.8);
&.dark {
@include opacity(1);
}
&.error {
- color: #a94442;
+ color: darken($red, 20%);
}
}
@@ -49,13 +49,9 @@
}
.help-block {
+ color: $dark-gray;
font-size: .95em;
margin: 10px 0 0;
- color: #999;
-}
-
-.disabled-input {
- background-color: #ddd !important;
}
.form-group {
diff --git a/webapp/sass/layout/_headers.scss b/webapp/sass/layout/_headers.scss
index 0a6b9f920..9b631d00f 100644
--- a/webapp/sass/layout/_headers.scss
+++ b/webapp/sass/layout/_headers.scss
@@ -1,6 +1,6 @@
@charset 'UTF-8';
-#channel-header {
+.channel-header {
@include flex(0 0 56px);
}
@@ -15,8 +15,8 @@
}
.header-dropdown__icon {
- font-size: 11px;
color: inherit;
+ font-size: 11px;
top: 3px;
}
@@ -31,12 +31,12 @@
word-break: break-word;
&.dropdown {
+ float: left;
max-width: 100%;
padding-right: 1em;
- float: left;
.header-dropdown__icon {
- color: #777;
+ color: $dark-gray;
}
a {
@@ -49,10 +49,11 @@
}
&.description {
- overflow: hidden;
- text-overflow: ellipsis;
margin-top: 2px;
max-height: 45px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+
.markdown-inline-img {
max-height: 45px;
}
@@ -65,14 +66,14 @@
}
.channel-intro {
+ border-bottom: 1px solid $light-gray;
margin: 0 auto 35px;
- padding: 0 1em 5px;
max-width: 1000px;
- border-bottom: 1px solid lightgrey;
+ padding: 0 1em 5px;
.intro-links {
- margin: 0 1.5em 10px 0;
display: inline-block;
+ margin: 0 1.5em 10px 0;
.fa {
margin-right: 5px;
@@ -80,28 +81,30 @@
}
.channel-intro-profile {
- margin-top: 5px;
margin-left: 63px;
+ margin-top: 5px;
}
.channel-intro-img {
float: left;
+
img {
@include border-radius(100px);
}
}
.channel-intro__title {
- font-weight: 600;
font-size: 20px;
+ font-weight: 600;
margin-bottom: 15px;
}
.channel-intro__content {
- background: #f7f7f7;
- padding: 10px 15px;
@include border-radius(3px);
+ background: $bg--gray;
+ padding: 10px 15px;
}
+
.channel-intro-text {
margin-top: 35px;
}
@@ -111,28 +114,28 @@
.sidebar--left,
.sidebar--menu {
.team__header {
- position: relative;
- padding: 9px 10px;
@include legacy-pie-clearfix;
+ padding: 9px 10px;
+ position: relative;
&:before {
@include single-transition(all, .05s, linear);
- content: '';
background: none;
- top: 0;
- left: 0;
- width: 100%;
+ content: '';
height: 100%;
+ left: 0;
position: absolute;
+ top: 0;
+ width: 100%;
}
&:hover {
&:before {
- background: rgba(black, .1);
+ @include alpha-property(background, $black, .1);
}
.user__name {
- color: #fff;
+ color: $white;
}
.navbar-right {
@@ -145,95 +148,88 @@
.navbar-right {
font-size: .85em;
position: absolute;
- top: 10px;
right: 22px;
+ top: 10px;
z-index: 5;
.dropdown-toggle {
+ @include opacity(.8);
@include single-transition(all, .1s, linear);
padding: 10px;
- @include opacity(.8);
}
.dropdown-menu {
- li a {
+ a {
+ overflow: hidden;
padding: 3px 20px;
text-overflow: ellipsis;
- overflow: hidden;
}
}
.dropdown__icon {
- fill: #fff;
+ fill: $white;
}
}
- .settings__link a:hover,
- a:visited,
- a:link,
- a:active {
- text-decoration: none;
- }
-
.user__picture {
- width: 36px;
- height: 36px;
- float: left;
@include border-radius(36px);
+ float: left;
+ height: 36px;
margin-right: 6px;
+ width: 36px;
}
.header__info {
- color: #fff;
@include clearfix;
+ color: $white;
padding-left: 2px;
- z-index: 1;
position: relative;
+ z-index: 1;
}
.team__name,
.user__name {
display: block;
- font-weight: 600;
font-size: 16px;
+ font-weight: 600;
max-width: 85%;
overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
text-decoration: none;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.team__name {
+ float: left;
line-height: 22px;
margin-top: -2px;
- float: left;
}
.user__name {
@include single-transition(all, .1s, linear);
+ @include alpha-property(color, $white, .8);
font-size: 14px;
- line-height: 18px;
font-weight: 400;
- color: #eee;
- color: rgba(#fff, .8);
+ line-height: 18px;
}
> .nav {
> li {
> a {
+ background: none;
float: right;
- background: none !important;
padding: 2px;
+ }
- &.dropdown-toggle {
- line-height: 1.8;
- font-size: 1em;
- color: #fff;
- }
+ .dropdown-toggle {
+ color: $white;
+ font-size: 1em;
+ line-height: 1.8;
}
}
}
}
+
.search__clear {
display: none;
}
@@ -250,27 +246,29 @@
}
.channel-header {
- width: 100%;
border-left: none;
font-size: 14px;
line-height: 56px;
+ width: 100%;
#member_popover {
- width: 50px;
- color: #999;
+ color: #999999;
cursor: pointer;
+ width: 50px;
.fa {
- margin-left: 4px;
font-size: 16px;
+ margin-left: 4px;
}
}
&.alt {
margin: 0;
+
th {
font-weight: normal !important;
}
+
td {
border: none;
}
@@ -278,37 +276,39 @@
th {
text-align: center;
+
&:first-child {
- text-align: left !important;
border-left: none;
- width: 100%;
padding-left: 1em;
+ text-align: left !important;
+ width: 100%;
}
+
&:last-child {
width: 8.9%;
}
}
td {
- padding: 5px 25px 5px !important;
font-size: 13px;
+ padding: 5px 25px 5px !important;
text-align: center !important;
+
&:first-child {
text-align: left !important;
}
}
.heading {
- margin: 0;
- color: #555;
+ color: #555555;
+ display: inline-block;
font-size: 1.3em;
font-weight: 600;
+ margin: 0 4px 0 0;
+ max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
- max-width: 100%;
- display: inline-block;
vertical-align: middle;
- margin-right: 4px;
}
.caret {
@@ -318,13 +318,13 @@
.more {
color: #81848b;
display: inline-block;
- vertical-align: top;
- text-transform: capitalize;
font-size: 13px;
+ text-transform: capitalize;
+ vertical-align: top;
}
.disabled {
- color: #999;
+ color: #999999;
}
}
@@ -342,11 +342,11 @@
.channel-header__links {
font-family: 'Open Sans', sans-serif;
+ font-size: 22px;
height: 30px;
- width: 24px;
line-height: 26px;
margin-right: 9px;
- font-size: 22px;
+ width: 24px;
.channel__wrap.move--left & {
position: absolute;
@@ -355,14 +355,15 @@
}
> a {
- color: inherit;
- text-decoration: none;
@include opacity(.6);
@include single-transition(all, .1s, ease-in);
+ color: inherit;
+ text-decoration: none;
&:hover {
@include opacity(1);
}
+
&:focus {
color: inherit;
}
diff --git a/webapp/sass/layout/_markdown.scss b/webapp/sass/layout/_markdown.scss
index 307060ac3..b9acd8b5b 100644
--- a/webapp/sass/layout/_markdown.scss
+++ b/webapp/sass/layout/_markdown.scss
@@ -16,43 +16,77 @@
#post-list {
.markdown-inline-img {
-moz-force-broken-image-icon: 1;
- max-height: 500px;
height: 500px;
+ max-height: 500px;
}
}
.post-body--code {
+ overflow-x: auto;
+ overflow-y: hidden;
position: relative;
pre {
- margin-bottom: 0;
- word-break: normal;
- overflow: auto;
- word-wrap: normal;
+ border: 1px solid rgba(221,221,221,0.2);
+ border-radius: .25em;
+ margin: 0;
+ padding: 0px;
+ text-align: left;
+ white-space: nowrap;
+ }
+
+ code {
+ border: none;
+ white-space: pre;
+ }
+
+ td {
+ padding: 0 .5em;
+ vertical-align: top;
+ }
+
+ &:hover .post-body--code__language {
+ @include opacity(1);
}
}
.post-body--code__language {
- -webkit-transform: translate3d(0,0,0);
+ @include opacity(0);
+ @include translate3d(0, 0, 0);
+ background: #21586d;
+ border-radius: 0 .25em;
+ color: $white;
+ padding: 4px 10px 5px;
position: absolute;
- top: 0;
right: 0;
- color: #fff;
- background: #21586d;
- padding: 4px 10px 5px 10px;
- font-size: 13px;
- opacity: .7;
+ top: 0;
+ -webkit-transition: opacity 0.6s;
+ -moz-transition: opacity 0.6s;
+ -o-transition: opacity 0.6s;
+ transition: opacity 0.6s;
z-index: 5;
}
+.post-body--code__lineno {
+ border-right: 1px solid #aaa;
+ color: #aaa;
+ margin-right: .5em;
+ text-align: right;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+}
+
.post__body {
hr {
- height: 4px;
- padding: 0;
- margin: 15px 0 16px;
+ @include opacity(.2);
background-color: #e7e7e7;
border: 0 none;
- @include opacity(.2);
+ height: 4px;
+ margin: 15px 0 16px;
+ padding: 0;
}
code {
@@ -67,50 +101,53 @@
}
.markdown__table {
- background: #fff;
+ background: $white;
margin: 5px 0 10px;
th,
td {
- padding: 6px 13px;
border: 1px solid #ddd;
+ padding: 6px 13px;
}
tbody tr {
- background: #fff;
+ background: $white;
&:nth-child(2n) {
background-color: #f8f8f8;
}
}
}
+
blockquote {
border: none;
- position: relative;
font-size: 16px;
- padding: 10px 10px 10px 38px;
margin-bottom: 0;
+ padding: 10px 10px 10px 38px;
+ position: relative;
&:before {
+ @include opacity(.6);
+ content: '\f10d';
+ display: inline-block;
font-family: FontAwesome;
- font-weight: normal;
+ font-size: 20px;
font-style: normal;
- display: inline-block;
- text-decoration: inherit;
- content: '\f10d';
+ font-weight: normal;
left: 8px;
- top: 5px;
position: absolute;
- font-size: 20px;
- @include opacity(.6);
+ text-decoration: inherit;
+ top: 5px;
}
}
+
pre {
- border: none;
- margin: 5px 0;
color: inherit;
+ margin: 5px 0;
+ padding: 0;
}
+
code {
- background: #fff;
+ background: $white;
color: inherit;
}
diff --git a/webapp/sass/layout/_navigation.scss b/webapp/sass/layout/_navigation.scss
index 65c62cb17..87e4b4d27 100644
--- a/webapp/sass/layout/_navigation.scss
+++ b/webapp/sass/layout/_navigation.scss
@@ -7,14 +7,14 @@
#navbar {
input {
- margin: 0px 5px 0px 2px;
+ margin: 0 5px 0 2px;
}
.navbar-default {
- position: absolute;
+ background: $primary-color;
border: none;
min-height: 45px;
- background: $color--primary;
+ position: absolute;
.navbar-nav {
> li {
@@ -30,19 +30,19 @@
}
.navbar-toggle {
- width: 43px;
- float: left;
border-color: transparent;
border-radius: 0;
+ fill: $white;
+ float: left;
+ height: 44px;
+ line-height: 48px;
margin: 0;
padding: 0 10px;
- line-height: 48px;
- height: 44px;
+ width: 43px;
z-index: 5;
- fill: #fff;
.icon-bar {
- background: #fff;
+ background: $white;
width: 21px;
}
@@ -51,7 +51,7 @@
}
.icon--white {
- color: #fff;
+ color: $white;
}
&:hover,
@@ -62,16 +62,16 @@
}
.navbar-brand {
- padding: 0 .5em;
- height: 45px;
- line-height: 45px;
float: none;
font-size: 16px;
+ height: 45px;
+ line-height: 45px;
+ padding: 0 .5em;
.heading {
- margin-right: 3px;
+ color: $white;
font-weight: 600;
- color: #fff;
+ margin-right: 3px;
}
.header-dropdown__icon {
@@ -79,36 +79,36 @@
}
.dropdown-toggle {
- color: #fff;
+ color: $white;
}
.description {
+ color: $white;
display: inline-block;
margin-right: .5em;
- color: #fff;
&.info-popover {
- width: 19px;
- height: 19px;
- background: url('../images/info__icon.png');
@include background-size(100% 100%);
- vertical-align: middle;
- top: -1px;
- position: relative;
+ background: url('../images/info__icon.png');
cursor: pointer;
+ height: 19px;
+ position: relative;
+ top: -1px;
+ vertical-align: middle;
+ width: 19px;
}
}
}
}
.sidebar-channel {
- white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
+ white-space: nowrap;
span {
- white-space: nowrap;
margin-left: 2px;
+ white-space: nowrap;
}
}
@@ -117,11 +117,13 @@
}
.more-channel-table {
- width: 100%;
border: 1px solid #dbdbdc;
+ width: 100%;
+
td {
padding: 7px;
}
+
button {
width: 110px;
}
@@ -129,9 +131,9 @@
}
.badge-notify {
- background: red;
- position: absolute;
+ background: $red;
left: 4px;
+ position: absolute;
top: 3px;
z-index: 100;
}
diff --git a/webapp/sass/layout/_post-right.scss b/webapp/sass/layout/_post-right.scss
index 595577564..529f73995 100644
--- a/webapp/sass/layout/_post-right.scss
+++ b/webapp/sass/layout/_post-right.scss
@@ -27,8 +27,8 @@
.post {
&.post--root {
- padding-bottom: 1.2em;
border-bottom: 1px solid #ddd;
+ padding-bottom: 1.2em;
.post__body {
background: transparent !important;
@@ -37,8 +37,8 @@
.post__header {
.col__reply {
- top: 0;
text-align: right;
+ top: 0;
}
}
@@ -48,13 +48,13 @@
}
hr {
- margin-bottom: 0;
border: none;
+ margin-bottom: 0;
}
.post-create__container {
- width: 100%;
margin-top: 10px;
+ width: 100%;
.textarea-wrapper {
min-height: 100px;
@@ -70,48 +70,46 @@
.msg-typing {
@include opacity(.7);
+ display: block;
float: left;
- margin-top: 3px;
font-size: 13px;
- line-height: 20px;
- min-width: 1px;
- display: block;
height: 20px;
+ line-height: 20px;
+ margin-top: 3px;
max-width: 230px;
+ min-width: 1px;
}
.post-create-footer {
- width: 100%;
padding: 5px 0 10px;
+ width: 100%;
}
.post-right-comments-upload-in-progress {
- padding: 6px 0;
color: #a8adb7;
margin-right: 10px;
+ padding: 6px 0;
}
}
}
+
.post-right-header {
- font-size: 1em;
- text-transform: uppercase;
+ border-bottom: $border-gray;
color: #999;
+ font-size: 1em;
font-weight: 400;
- color: #888;
height: 39px;
padding: 10px 10px 0 15px;
- border-bottom: $border-gray;
+ text-transform: uppercase;
}
.post-right-root-container {
- padding: 5px 10px;
border-bottom: $border-gray;
-}
+ padding: 5px 10px;
-.post-right-root-container {
ul {
- padding-left: 0;
margin-bottom: 2px;
+ padding-left: 0;
}
}
@@ -131,19 +129,19 @@
}
.post-right-create-comment-container {
- padding: 5px;
- margin-bottom: 18px;
- position: absolute;
bottom: 0;
left: 0;
+ margin-bottom: 18px;
+ padding: 5px;
+ position: absolute;
width: 100%;
}
.post-right__scroll {
- position: relative;
- overflow: auto;
- -webkit-overflow-scrolling: touch;
@include flex(1 1 auto);
+ -webkit-overflow-scrolling: touch;
+ overflow: auto;
+ position: relative;
.file-preview__container {
margin-top: 5px;
diff --git a/webapp/sass/layout/_post.scss b/webapp/sass/layout/_post.scss
index ce055035d..9d5be239a 100644
--- a/webapp/sass/layout/_post.scss
+++ b/webapp/sass/layout/_post.scss
@@ -1,19 +1,20 @@
@charset 'UTF-8';
.custom-textarea {
- white-space: pre-wrap;
- word-wrap: break-word;
background: transparent;
- border: 1px solid #ccc;
- position: absolute;
- top: 0px;
+ border: 1px solid #cccccc;
height: auto;
- resize: none;
line-height: 20px;
min-height: 36px;
overflow-x: hidden;
+ position: absolute;
+ resize: none;
+ top: 0px;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+
&:focus {
- border-color: #ccc;
+ border-color: #cccccc;
box-shadow: none;
}
}
@@ -24,23 +25,23 @@
}
.textarea-div {
- white-space: pre-wrap;
- word-wrap: normal;
+ border: 0;
color: rgba(0,0,0,0);
- position: absolute;
- top: 0px;
- word-break: break-word;
+ height: auto;
left: 1px;
line-height: 20px;
min-height: 36px;
- height: auto;
- border: 0;
+ position: absolute;
+ top: 0px;
+ white-space: pre-wrap;
+ word-break: break-word;
+ word-wrap: normal;
}
body.ios {
.textarea-div {
- padding: 7px 17px 7px 15px;
-webkit-overflow-scrolling: auto;
+ padding: 7px 17px 7px 15px;
}
}
@@ -49,133 +50,146 @@ body.ios {
}
.textarea-wrapper {
+ min-height: 36px;
position: relative;
+
.textbox-preview-area {
+ box-shadow: none;
+ left: 0;
position: absolute;
- z-index: 2;
top: 0;
- left: 0;
- box-shadow: none;
white-space: normal;
+ z-index: 2;
}
+
.help__text {
- right: 0;
- position: absolute;
- z-index: 3;
bottom: -23px;
- font-size: 13px;
cursor: pointer;
+ font-size: 13px;
+ position: absolute;
+ right: 0;
+ z-index: 3;
}
+
.textbox-preview-link {
margin-right: 8px;
}
- min-height: 36px;
}
.help_format_text {
- display: none !important;
- position: absolute;
+ @include opacity(0);
+ @include single-transition(all .2s ease);
bottom: -23px;
- left: 0px;
+ display: none !important;
+ font-size: .85em;
+ left: 0;
overflow: hidden;
+ position: absolute;
text-overflow: ellipsis;
- font-size: .85em;
- @include opacity(0);
- @include single-transition(all .2s ease);
b,
i,
code {
margin-right: 3px;
}
+
.textbox-preview-link {
- font-size: 13px;
cursor: pointer;
+ font-size: 13px;
margin-left: 15px;
}
}
.date-separator,
.new-separator {
- text-align: center;
height: 2em;
margin: 0;
position: relative;
+ text-align: center;
z-index: 0;
+
&:before,
&:after {
content: '';
+ display: none;
height: 1em;
- position: absolute;
left: 0;
+ position: absolute;
width: 100%;
- display: none;
}
+
&:before {
bottom: 0;
}
+
&:after {
top: 0;
}
+
&.hovered--after {
&:before {
background: #f5f5f5;
display: block;
}
}
+
&.hovered--before {
&:after {
background: #f5f5f5;
display: block;
}
}
+
.separator__hr {
- border-color: #ccc;
+ border-color: #cccccc;
margin: 0;
position: relative;
- z-index: 5;
top: 1em;
+ z-index: 5;
}
+
.separator__text {
- line-height: 2em;
- color: #555;
- background: #fff;
+ @include border-radius(50px);
+ background: #ffffff;
+ color: #555555;
display: inline-block;
- padding: 0 1em;
+ font-size: 13px;
font-weight: 700;
- @include border-radius(50px);
+ line-height: 2em;
+ padding: 0 1em;
position: relative;
z-index: 5;
- font-size: 13px;
}
}
+
.new-separator {
.separator__hr {
border-color: #ffaf53;
}
+
.separator__text {
- color: #f80;
+ color: #ff8800;
font-weight: normal;
}
}
.file-overlay {
+ color: #ffffff;
+ font-size: em(20px);
+ font-weight: 600;
+ height: 100%;
+ left: 0;
position: absolute;
+ text-align: center;
top: 0;
- left: 0;
width: 100%;
- height: 100%;
- text-align: center;
- color: #fff;
- font-size: em(20px);
- font-weight: 600;
z-index: 6;
.overlay__indent {
- background-color: rgba(0, 0, 0, .6);
- position: relative;
- height: 100%;
@include clearfix;
+ @include alpha-property(background-color, $black, .6);
+ height: 100%;
+ position: relative;
}
&.center-file-overlay {
@@ -186,11 +200,13 @@ body.ios {
&.right-file-overlay {
font-size: em(18px);
+
.overlay__circle {
- width: 300px;
height: 300px;
margin: -150px 0 0 -150px;
+ width: 300px;
}
+
.overlay__files {
margin: 60px auto 15px;
width: 150px;
@@ -198,16 +214,15 @@ body.ios {
}
.overlay__circle {
- background: #111;
- background: rgba(black, .7);
- width: 370px;
+ @include border-radius(500px);
+ @include alpha-property(background, $black, .7);
height: 370px;
+ left: 50%;
margin: -185px 0 0 -185px;
- @include border-radius(500px);
+ pointer-events: none;
position: absolute;
top: 50%;
- left: 50%;
- pointer-events: none;
+ width: 370px;
}
.overlay__files {
@@ -216,11 +231,11 @@ body.ios {
}
.overlay__logo {
- position: absolute;
- left: 50%;
+ @include opacity(.3);
bottom: 30px;
+ left: 50%;
margin-left: -50px;
- @include opacity(.3);
+ position: absolute;
}
.fa {
@@ -232,62 +247,64 @@ body.ios {
#post-list {
@include flex(1 1 auto);
- position: relative;
- overflow-y: hidden;
height: 100%;
+ overflow-y: hidden;
+ position: relative;
.inactive {
display: none;
}
.post-list-holder-by-time {
- background: #fff;
+ -webkit-overflow-scrolling: touch;
+ background: #ffffff;
+ height: 100%;
overflow-y: scroll;
- width: 100%;
padding: 1em 0 0;
position: absolute;
- height: 100%;
- -webkit-overflow-scrolling: touch;
+ width: 100%;
+
&.active {
display: inline;
}
}
.more-messages-text {
- margin: 5px 0 10px;
- display: block;
- text-align: center;
- outline: none;
border: none;
+ display: block;
font-size: 13px;
+ margin: 5px 0 10px;
+ outline: none;
+ text-align: center;
}
+
.beginning-messages-text {
- margin-top: 2px;
- margin-bottom: 5px;
+ color: grey;
display: block;
+ margin-bottom: 5px;
+ margin-top: 2px;
text-align: center;
- color: grey;
}
}
.post-list__timestamp {
- position: absolute;
- top: 8px;
- left: 50%;
- z-index: 50;
- width: 120px;
- text-align: center;
- background: $color--primary;
- color: #fff;
@include border-radius(3px);
- font-size: 12px;
- line-height: 25px;
- margin-left: -60px;
- -webkit-font-smoothing: initial;
+ @include opacity(0);
@include single-transition(all, .6s, ease);
@include translateY(-45px);
- @include opacity(0);
+ @include font-smoothing(initial);
+ background: $primary-color;
+ color: $white;
display: none;
+ font-size: 12px;
+ left: 50%;
+ line-height: 25px;
+ margin-left: -60px;
+ position: absolute;
+ text-align: center;
+ top: 8px;
+ width: 120px;
+ z-index: 50;
&.scrolling {
@include translateY(0);
@@ -296,147 +313,162 @@ body.ios {
}
.post-list__arrows {
+ @include opacity(0);
+ @include single-transition(all, .6s);
background-repeat: no-repeat;
- width: 40px;
- height: 40px;
- text-align: center;
- fill: #444;
- position: absolute;
bottom: 0;
+ display: none;
+ fill: #444444;
+ height: 40px;
left: 9px;
+ position: absolute;
+ text-align: center;
+ width: 40px;
z-index: 50;
- @include opacity(0);
- @include single-transition(all, .6s);
- display: none;
svg {
color: inherit;
- width: 28px;
height: 28px;
+ width: 28px;
}
&.scrolling {
- display: block;
@include opacity(1);
+ display: block;
}
}
.post-create__container {
form {
- width: 100%;
- padding: .5em 14px 0;
margin: 0 auto;
max-width: 1028px;
+ padding: .5em 14px 0;
+ width: 100%;
}
+
.post-create-body {
- position: relative;
padding: 0 0 2px;
+ position: relative;
+
.post-body__cell {
- vertical-align: top;
position: relative;
+ vertical-align: top;
+
&.scroll {
.btn-file {
right: 15px;
}
+
.custom-textarea {
padding-right: 43px;
}
}
}
+
.send-button {
- display: none;
+ @include single-transition(all, .15s);
cursor: pointer;
- padding-right: 4px;
- width: 45px;
- height: 37px;
+ display: none;
font-size: 18px;
+ height: 37px;
line-height: 37px;
- vertical-align: bottom;
+ padding-right: 4px;
text-align: center;
- @include single-transition(all, .15s);
+ vertical-align: bottom;
+ width: 45px;
+
&:active {
@include opacity(.75);
}
}
+
.custom-textarea {
- padding-top: 8px;
- padding-right: 28px;
- max-height: 162px !important;
line-height: 1.5;
+ max-height: 162px !important;
+ padding-right: 28px;
+ padding-top: 8px;
}
+
.textarea-div {
- padding-top: 8px;
- padding-right: 30px;
+ line-height: 1.5;
max-height: 163px !important;
overflow: auto;
- line-height: 1.5;
+ padding-right: 30px;
+ padding-top: 8px;
}
+
.btn-file {
- right: 0;
- position: absolute;
- top: 1px;
- color: #444;
@include opacity(.5);
@include single-transition(all, .15s);
+ color: #444444;
font-size: 16px;
padding: 7px 9px 6px;
+ position: absolute;
+ right: 0;
+ top: 1px;
+
&:hover,
&:active {
@include opacity(.9);
box-shadow: none;
}
}
+
textarea {
box-shadow: none;
}
}
+
.post-create-footer {
@include clearfix;
- padding: 3px 0 0 0;
font-size: 13px;
+ padding: 3px 0 0 0;
.control-label {
font-weight: normal;
margin-bottom: 0;
- top: -5px;
position: relative;
+ top: -5px;
}
}
+
.msg-typing {
- display: block;
@include opacity(.7);
- white-space: nowrap;
+ display: block;
+ font-size: .95em;
+ height: 20px;
margin-bottom: 5px;
overflow: hidden;
- font-size: .95em;
text-overflow: ellipsis;
- height: 20px;
+ white-space: nowrap;
}
}
.post-list__table {
display: table;
+ height: 100%;
+ min-height: 100%;
table-layout: fixed;
width: 100%;
- min-height: 100%;
- height: 100%;
+
.post-list__content {
display: table-cell;
vertical-align: bottom;
+
.dropdown-menu {
&.bottom {
- top: auto;
bottom: 25px;
+ top: auto;
}
}
}
}
.post {
- word-wrap: break-word;
+ @include legacy-pie-clearfix;
+ max-width: 100%;
padding: 8px .5em 0 1em;
position: relative;
- max-width: 100%;
- @include legacy-pie-clearfix;
+ word-wrap: break-word;
&:hover {
.dropdown,
@@ -445,6 +477,7 @@ body.ios {
.post__remove {
visibility: visible;
}
+
.permalink-icon {
visibility: visible;
}
@@ -455,9 +488,9 @@ body.ios {
}
p {
- margin: 0;
- line-height: 1.6em;
font-size: .97em;
+ line-height: 1.6em;
+ margin: 0;
white-space: pre-wrap;
&:last-child {
@@ -488,8 +521,8 @@ body.ios {
}
.post__header {
- margin: 0;
height: 0;
+ margin: 0;
.col__name {
display: none;
@@ -501,20 +534,13 @@ body.ios {
}
.post__time {
- top: 24px;
- }
-
- .post__time {
- font: normal normal normal FontAwesome;
- text-rendering: auto;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- position: absolute;
- top: -2px;
- left: -7px;
+ @include opacity(0);
font-size: 11px;
+ left: -7px;
line-height: 37px;
- @include opacity(0);
+ position: absolute;
+ text-rendering: auto;
+ top: -2px;
}
}
@@ -528,11 +554,9 @@ body.ios {
display: none;
}
}
- }
- &.post--comment {
.post__body {
- border-left: 4px solid #ddd;
+ border-left: 4px solid #dddddd;
}
}
}
@@ -562,12 +586,12 @@ body.ios {
}
.post__content {
+ display: table;
margin: 0 auto;
- position: relative;
max-width: 1000px;
- display: table;
- width: 100%;
+ position: relative;
table-layout: fixed;
+ width: 100%;
> div {
display: table-cell;
@@ -576,9 +600,9 @@ body.ios {
}
.post__header {
- padding: 0;
list-style: none;
margin-bottom: 2px;
+ padding: 0;
li {
display: inline-block;
@@ -586,12 +610,12 @@ body.ios {
}
.col__name {
- margin-right: 7px;
font-weight: 600;
+ margin-right: 7px;
.user-popover {
- max-width: 200px;
@include clearfix;
+ max-width: 200px;
text-overflow: ellipsis;
white-space: nowrap;
}
@@ -601,8 +625,8 @@ body.ios {
position: absolute;
right: 0;
top: 30px;
- width: 65px;
white-space: nowrap;
+ width: 65px;
}
.permalink-popover {
@@ -616,27 +640,29 @@ body.ios {
.btn {
font-size: 13px;
height: 30px;
- padding: 0 8px;
line-height: 30px;
+ padding: 0 8px;
}
}
}
.post__img {
width: 46px;
+
img {
- width: 36px;
+ @include border-radius(50px);
height: 36px;
vertical-align: inherit;
- @include border-radius(50px);
+ width: 36px;
}
}
.post__embed-container {
+ @include single-transition(max-height, .5, ease);
display: block;
max-height: 1000px;
- transition: max-height .5s ease;
overflow: hidden;
+
&[hidden] {
max-height: 0;
}
@@ -644,15 +670,15 @@ body.ios {
.dropdown {
display: inline-block;
- visibility: hidden;
margin-right: 5px;
top: -1px;
+ visibility: hidden;
.dropdown-menu {
- right: 0;
left: auto;
min-width: 130px;
padding: 2px 0;
+ right: 0;
li {
display: block;
@@ -667,27 +693,27 @@ body.ios {
.post__dropdown {
&:after {
content: '[...]';
- top: -1px;
position: relative;
+ top: -1px;
}
}
.post__remove {
- font-family: 'Open Sans', sans-serif;
- position: relative;
+ @include opacity(.5);
+ color: inherit;
display: inline-block;
- vertical-align: top;
- right: 15px;
- top: -5px;
+ font-family: 'Open Sans', sans-serif;
font-size: 20px;
- width: 20px;
+ font-weight: 600;
height: 20px;
line-height: 20px;
- font-weight: 600;
- visibility: hidden;
- color: inherit;
- @include opacity(.5);
+ position: relative;
+ right: 15px;
text-decoration: none;
+ top: -5px;
+ vertical-align: top;
+ visibility: hidden;
+ width: 20px;
&:hover {
@include opacity(.8);
@@ -695,10 +721,10 @@ body.ios {
}
.post__body {
- word-wrap: break-word;
- padding: .2em .5em;
@include legacy-pie-clearfix;
+ padding: .2em .5em;
width: calc(100% - 75px);
+ word-wrap: break-word;
p {
margin: 0 0 .4em;
@@ -725,6 +751,7 @@ body.ios {
p {
margin-bottom: 0;
}
+
li ul,
li ol {
padding: 0 0 0 20px;
@@ -745,8 +772,8 @@ body.ios {
}
li input[type='checkbox']:disabled {
- vertical-align: top;
cursor: default;
+ vertical-align: top;
}
}
@@ -766,21 +793,21 @@ body.ios {
}
.post__link {
- margin: 2px 0 5px;
font-size: 13px;
- text-overflow: ellipsis;
+ margin: 2px 0 5px;
overflow: hidden;
+ text-overflow: ellipsis;
white-space: nowrap;
}
.post__embed-visibility {
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
cursor: pointer;
+ display: inline-block;
font: normal normal normal 14px/1 FontAwesome;
- text-rendering: auto;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
margin: 5px 0 10px;
- display: inline-block;
+ text-rendering: auto;
&:hover {
text-decoration: none;
@@ -807,8 +834,8 @@ body.ios {
.post-loading-gif {
height: 10px;
- width: 10px;
margin-top: 6px;
+ width: 10px;
}
.post-fail {
@@ -816,12 +843,12 @@ body.ios {
}
.post-waiting {
- color: #999;
+ color: #999999;
}
.permalink-icon {
+ color: $primary-color;
display: inline-block;
- color: $color--primary;
visibility: hidden;
}
@@ -830,16 +857,16 @@ body.ios {
margin-right: 6px;
visibility: hidden;
svg {
- width: 18px;
- top: 3px;
fill: inherit;
position: relative;
+ top: 3px;
+ width: 18px;
}
}
.comment-icon__container {
- fill: $color--primary;
display: inline-block;
+ fill: $primary-color;
visibility: hidden;
&:focus {
@@ -857,10 +884,10 @@ body.ios {
.comment-icon {
display: inline-block;
- top: 2px;
- position: relative;
- margin-right: 3px;
fill: inherit;
+ margin-right: 3px;
+ position: relative;
+ top: 2px;
}
path {
@@ -869,28 +896,30 @@ body.ios {
}
.web-embed-data {
- padding: 2px 0 0 10px;
- background: #f9f9f9;
- background: rgba(0, 0, 0, .05);
@include border-radius(2px);
+ @include alpha-property(background, $black, 0.05);
height: 50px;
overflow: hidden;
+ padding: 2px 0 0 10px;
text-overflow: ellipsis;
+
.embed-title {
- margin: 3px 0 1px;
- color: #555;
+ color: #555555;
font-weight: 600;
- white-space: nowrap;
- text-overflow: ellipsis;
+ margin: 3px 0 1px;
overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
+
.embed-description {
+ color: #888888;
margin: 0;
- white-space: nowrap;
- text-overflow: ellipsis;
overflow: hidden;
- color: #888;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
+
.embed-link {
display: none;
}
@@ -898,89 +927,104 @@ body.ios {
}
.bot-indicator {
+ border-radius: 2px;
font-family: inherit;
font-size: 11px;
- padding: 2px 4px;
- border-radius: 2px;
font-weight: 600;
margin: 0 0 0 -4px;
+ padding: 2px 4px;
}
.attachment {
.attachment__content {
- border-width: 1px;
- border-style: solid;
border-radius: 4px;
- padding: 2px 5px;
+ border-style: solid;
+ border-width: 1px;
margin: 0 0 5px 0;
+ padding: 2px 5px;
}
+
.attachment__thumb-pretext {
- border: 0 none;
background: transparent;
+ border: none;
+ margin-left: 5px;
}
+
.attachment__container {
- border-left-width: 4px;
border-left-style: solid;
+ border-left-width: 4px;
padding: 2px 0 2px 10px;
+
&.attachment__container--good {
border-left-color: #00c100;
}
+
&.attachment__container--warning {
border-left-color: #dede01;
}
+
&.attachment__container--danger {
border-left-color: #e40303;
}
}
+
.attachment__body {
float: left;
- width: 80%;
- padding-right: 5px;
overflow-x: auto;
overflow-y: hidden;
+ padding-right: 5px;
+ width: 80%;
+
&.attachment__body--no_thumb {
width: 100%;
}
}
+
.attachment__text p:last-of-type {
display: inline-block;
}
- .attachment__thumb-pretext {
- margin-left: 5px;
- }
+
.attachment__title {
+ font-size: 16px;
+ height: 22px;
+ line-height: 16px;
margin: 5px 0;
padding: 0;
- line-height: 16px;
- height: 22px;
- font-size: 16px;
+
a {
font-size: 16px;
}
}
+
.attachment__author-icon {
@include border-radius(50px);
+ height: 14px;
margin-right: 5px;
width: 14px;
- height: 14px;
}
+
.attachment__image {
- max-width: 100%;
margin-bottom: 1em;
+ max-width: 100%;
}
+
.attachment__thumb-container {
- width: 20%;
float: right;
+ width: 20%;
+
img {
max-height: 75px;
max-width: 100%;
}
}
+
.attachment___fields {
width: 100%;
+
.attachment___field-caption {
font-weight: 700;
}
+
.attachment___field p {
margin: 0;
}
@@ -992,6 +1036,6 @@ body.ios {
}
.permalink-popover {
- min-width: 320px;
margin-left: 50px !important;
+ min-width: 320px;
}
diff --git a/webapp/sass/layout/_sidebar-left.scss b/webapp/sass/layout/_sidebar-left.scss
index ece054a15..4c65d0a65 100644
--- a/webapp/sass/layout/_sidebar-left.scss
+++ b/webapp/sass/layout/_sidebar-left.scss
@@ -1,86 +1,97 @@
@charset 'UTF-8';
.sidebar--left {
+ background: #fafafa;
+ border-right: $border-gray;
+ height: 100%;
+ left: 0;
position: absolute;
width: 220px;
- left: 0;
- height: 100%;
- border-right: $border-gray;
- background: #fafafa;
z-index: 5;
&.sidebar--padded {
padding-top: 44px;
}
+
.dropdown-menu {
max-height: 400px;
+ max-width: 200px;
overflow-x: hidden;
overflow-y: auto;
- max-width: 200px;
width: 200px;
}
+
.search__form {
+ display: none;
margin: 0;
padding: 1em 1em 0;
- display: none;
}
+
.badge {
- background-color: $color--primary;
+ background-color: $primary-color;
position: absolute;
right: 10px;
top: 5px;
}
+
.status {
+ display: inline-block;
+ margin-right: 6px;
position: relative;
top: 1px;
- margin-right: 6px;
width: 12px;
- display: inline-block;
+
svg {
max-height: 14px;
}
+
i,
path,
ellipse {
@include opacity(.5);
+
&.online--icon,
&.away--icon {
@include opacity(1);
}
}
+
.fa-lock {
margin-left: 1px;
}
+
.fa-globe {
- top: -1px;
position: relative;
+ top: -1px;
}
}
+
.nav-pills__container {
+ -webkit-overflow-scrolling: touch;
height: calc(100% - 80px);
- position: relative;
overflow: auto;
- -webkit-overflow-scrolling: touch;
+ position: relative;
}
.nav-pills__unread-indicator {
- position: absolute;
- left: 0;
- right: 0;
- width: 72%;
- color: #fff;
- background: #2389d7;
@include border-radius(50px);
+ background: #2389d7;
+ color: #ffffff;
+ font-size: 13.5px;
+ left: 0;
margin: 0 auto;
padding: 3px 0 4px;
- font-size: 13.5px;
+ position: absolute;
+ right: 0;
text-align: center;
+ width: 72%;
z-index: 1;
}
.nav-pills__unread-indicator-top {
top: 66px;
}
+
.nav-pills__unread-indicator-bottom {
bottom: 20px;
}
@@ -91,71 +102,83 @@
margin: 0;
}
}
+
li {
> h4 {
+ color: #aaaaaa;
font-size: 1em;
- text-transform: uppercase;
- margin: 1.1em 0 .5em;
font-weight: 400;
- color: #aaa;
letter-spacing: -.3px;
+ margin: 1.1em 0 .5em;
padding: 0 10px 0 15px;
+ text-transform: uppercase;
}
+
> a {
- padding: 3px 10px 3px 25px;
- line-height: 1.5;
border-radius: 0;
- white-space: nowrap;
+ line-height: 1.5;
overflow: hidden;
+ padding: 3px 10px 3px 25px;
text-overflow: ellipsis;
+ white-space: nowrap;
+
&.has-badge {
padding-right: 30px;
}
+
&.has-close {
padding-right: 30px;
+
&:hover {
.btn-close {
- display: block;
@include opacity(.8);
+ display: block;
}
}
+
.btn-close {
+ @include opacity(0);
+ display: none;
font-family: 'Open Sans', sans-serif;
+ font-size: 20px;
+ font-weight: 600;
position: absolute;
right: 15px;
top: -1px;
- font-size: 20px;
- font-weight: 600;
- @include opacity(0);
- display: none;
+
&:hover {
@include opacity(1);
}
}
}
+
&.nav-more {
text-decoration: underline;
}
+
&.unread-title {
font-weight: 600;
}
+
}
+
&.active {
a {
&:before {
+ background: $black;
content: '';
+ height: 100%;
+ left: 0;
position: absolute;
top: 0;
- left: 0;
width: 5px;
- height: 100%;
- background: #000;
}
}
+
a,
a:hover,
a:focus {
- background-color: rgba(black, .1);
+ @include alpha-property(background-color, $black, .1);
border-radius: 0;
font-weight: 400;
position: relative;
@@ -163,35 +186,37 @@
}
}
}
+
.modal-title {
line-height: 2em;
}
.add-channel-btn {
+ color: #aaaaaa;
float: right;
- outline: none;
+ font-size: 22px;
+ font-weight: 700;
+ line-height: 18px;
margin: -2px 0 0 0;
- color: #aaa;
+ outline: none;
padding: 0 5px;
text-decoration: none;
- font-size: 22px;
- line-height: 18px;
- font-weight: 700;
+
&:hover {
- color: #666;
+ color: #666666;
}
}
}
.channel-loading-gif {
height: 15px;
- width: 15px;
margin-top: 2px;
+ width: 15px;
}
.join-channel-loading-gif {
- margin-top: 5px;
- margin-left: 10px;
height: 25px;
+ margin-left: 10px;
+ margin-top: 5px;
width: 25px;
}
diff --git a/webapp/sass/layout/_sidebar-menu.scss b/webapp/sass/layout/_sidebar-menu.scss
index 9438491f9..0cf60f328 100644
--- a/webapp/sass/layout/_sidebar-menu.scss
+++ b/webapp/sass/layout/_sidebar-menu.scss
@@ -1,69 +1,79 @@
@charset 'UTF-8';
.sidebar--menu {
- position: absolute;
- width: 220px;
- right: 0;
- height: 100%;
- border-right: $border-gray;
- padding: 0 0 2em 0;
background: #fafafa;
+ border-right: $border-gray;
display: none;
+ height: 100%;
+ padding: 0 0 2em;
+ position: absolute;
+ right: 0;
+ width: 220px;
+
.nav-pills__container {
padding-top: 5px;
}
+
.team__header {
+ @include legacy-pie-clearfix;
display: none;
padding: 0 15px;
- @include legacy-pie-clearfix;
+
a {
- color: #fff;
+ color: $white;
}
+
.navbar-right {
font-size: .85em;
margin: 16px -5px 0;
+
.dropdown-toggle {
padding: 0 10px;
}
+
.dropdown-menu {
li a {
- padding: 3 20px;
color: #555;
+ padding: 3 20px;
}
}
+
.dropdown__icon {
- width: 4px;
- height: 16px;
@include background-size(100% 100%);
display: inline-block;
+ height: 16px;
+ width: 4px;
}
}
+
.team__name {
float: left;
- line-height: 50px;
- font-weight: 600;
font-size: 1.2em;
+ font-weight: 600;
+ line-height: 50px;
max-width: 80%;
overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
text-decoration: none;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
}
+
.nav {
> li {
> a {
- font-size: 15px;
background: none !important;
color: inherit;
+ font-size: 15px;
line-height: 40px;
padding: 0 15px;
+
.fa,
.glyphicon {
- width: 25px;
- text-align: center;
left: -5px;
position: relative;
+ text-align: center;
+ width: 25px;
}
}
}
diff --git a/webapp/sass/layout/_sidebar-right.scss b/webapp/sass/layout/_sidebar-right.scss
index e1a7d7641..a7b631047 100644
--- a/webapp/sass/layout/_sidebar-right.scss
+++ b/webapp/sass/layout/_sidebar-right.scss
@@ -1,13 +1,13 @@
@charset 'UTF-8';
.sidebar--right {
- position: fixed;
- width: 400px;
+ @include translateX(400px);
+ background: $white;
height: 100%;
- right: 0px;
padding: 0;
- background: #fff;
- @include translateX(400px);
+ position: fixed;
+ right: 0;
+ width: 400px;
.post-body {
img {
@@ -26,39 +26,39 @@
}
.sidebar--right__content {
- height: 100%;
@include display-flex;
@include flex-direction(column);
+ height: 100%;
}
.sidebar--right__back {
- color: inherit;
@include opacity(.8);
- width: 30px;
- text-align: center;
- margin: 0 0 0 -14px;
- font-size: 13px;
+ color: inherit;
display: inline-block;
+ font-size: 13px;
+ margin: 0 0 0 -14px;
+ text-align: center;
+ width: 30px;
}
.sidebar-right__body {
+ @include border-radius(2px 0 0 0);
+ @include display-flex;
@include flex(1 1 auto);
+ @include flex-direction(column);
border-left: $border-gray;
border-top: $border-gray;
- @include display-flex;
- @include flex-direction(column);
height: calc(100% - 56px);
- @include border-radius(2px 0 0 0);
}
.sidebar__overlay {
- width: 100%;
- height: 100%;
- background-color: yellow;
@include opacity(.1);
+ background-color: $yellow;
+ height: 100%;
+ pointer-events: none;
position: absolute;
+ width: 100%;
z-index: 5;
- pointer-events: none;
}
.input-group {
@@ -66,17 +66,17 @@
}
.sidebar--right__close {
- margin: 11px 0 0 0;
- width: 22px;
- height: 22px;
- opacity: .5;
- font-size: 22px;
- line-height: 0;
+ @include single-transition(all, .2s, ease-in);
background: none;
+ border: none;
float: right;
+ font-size: 22px;
+ height: 22px;
+ line-height: 0;
+ margin: 11px 0 0;
+ opacity: .5;
outline: none;
- border: none;
- @include single-transition(all, .2s, ease-in);
+ width: 22px;
&:hover,
&:active {
@@ -90,14 +90,14 @@
}
.sidebar--right__header {
- font-size: 1em;
- text-transform: uppercase;
+ @include flex(0 0 44px);
+ border-bottom: $border-gray;
color: inherit;
+ font-size: 1em;
height: 44px;
- padding: 0 1em;
line-height: 44px;
- @include flex(0 0 44px);
- border-bottom: $border-gray;
+ padding: 0 1em;
+ text-transform: uppercase;
}
.sidebar--right__subheader {
diff --git a/webapp/sass/layout/_webhooks.scss b/webapp/sass/layout/_webhooks.scss
index d6e367218..ffd8dd7dc 100644
--- a/webapp/sass/layout/_webhooks.scss
+++ b/webapp/sass/layout/_webhooks.scss
@@ -1,11 +1,11 @@
@charset 'UTF-8';
.webhooks__container {
- background: rgba(black, .1);
- border: 1px solid;
@include border-radius(3px);
- padding: 0 13px 15px;
+ @include alpha-property(background, $black, .1);
+ border: 1px solid;
margin-top: 10px;
+ padding: 0 13px 15px;
}
.webhook__item {
@@ -19,16 +19,16 @@
}
.webhook__remove {
- position: absolute;
- right: -7px;
- top: 8px;
- width: 30px;
- height: 30px;
+ color: #e05f5d;
font-size: 22px;
font-weight: bold;
+ height: 30px;
+ position: absolute;
+ right: -7px;
text-align: center;
text-decoration: none;
- color: #e05f5d;
+ top: 8px;
+ width: 30px;
}
.webhook__url {
diff --git a/webapp/sass/responsive/_desktop.scss b/webapp/sass/responsive/_desktop.scss
index dcabb807f..ccd6f0226 100644
--- a/webapp/sass/responsive/_desktop.scss
+++ b/webapp/sass/responsive/_desktop.scss
@@ -32,21 +32,23 @@
.tip-overlay {
&.tip-overlay--chat {
margin: -10px 0 0 -10px;
+
.arrow {
- right: 15px;
left: auto;
+ right: 15px;
}
}
}
+
.inner-wrap {
&.move--left {
.file-overlay {
font-size: em(18px);
.overlay__circle {
- width: 300px;
height: 300px;
margin: -150px 0 0 -150px;
+ width: 300px;
}
.overlay__files {
@@ -62,4 +64,4 @@
.modal-lg {
width: 700px;
}
-} \ No newline at end of file
+}
diff --git a/webapp/sass/responsive/_mobile.scss b/webapp/sass/responsive/_mobile.scss
index 4a1397143..53ea6d329 100644
--- a/webapp/sass/responsive/_mobile.scss
+++ b/webapp/sass/responsive/_mobile.scss
@@ -519,7 +519,7 @@
.search-bar__container {
padding: 0;
@include flex(0 0 44px);
- background: $color--primary;
+ background: $primary-color;
color: #fff;
&.focused {
@@ -756,9 +756,11 @@
.app__content {
padding-top: 45px;
margin: 0;
+
.channel__wrap & {
padding-top: 45px;
}
+
#channel-header {
display: none;
}
@@ -775,11 +777,6 @@
.post-comments {
padding: 9px 21px 10px 10px !important;
}
-
- .post-image__column .post-image .file-playback__controls.stop,
- .image-wrapper > a .file-playback__controls.stop {
- @include opacity(1);
- }
}
@media screen and (max-width: 640px) {
diff --git a/webapp/sass/responsive/_tablet.scss b/webapp/sass/responsive/_tablet.scss
index 14641cfa5..0a725a558 100644
--- a/webapp/sass/responsive/_tablet.scss
+++ b/webapp/sass/responsive/_tablet.scss
@@ -2,9 +2,9 @@
@media screen and (max-width: 960px) {
.sidebar--right {
- z-index: 5;
- @include translateX(100%);
@include single-transition(all, .5s, ease);
+ @include translateX(100%);
+ z-index: 5;
&.move--left {
@include translateX(0);
@@ -32,4 +32,4 @@
.second-bar {
display: none;
}
-} \ No newline at end of file
+}
diff --git a/webapp/sass/routes/_access-history.scss b/webapp/sass/routes/_access-history.scss
index 7ede7b085..ea66f5f00 100644
--- a/webapp/sass/routes/_access-history.scss
+++ b/webapp/sass/routes/_access-history.scss
@@ -2,9 +2,9 @@
.access-history__table {
display: table;
- width: 100%;
- padding-top: 15px;
line-height: 1.6;
+ padding-top: 15px;
+ width: 100%;
&:first-child {
padding: 0;
@@ -16,8 +16,8 @@
}
.access__date {
- font-weight: 600;
font-size: 15px;
+ font-weight: 600;
width: 190px;
}
diff --git a/webapp/sass/routes/_activity-log.scss b/webapp/sass/routes/_activity-log.scss
index 6e8d0a925..d3400e29a 100644
--- a/webapp/sass/routes/_activity-log.scss
+++ b/webapp/sass/routes/_activity-log.scss
@@ -1,36 +1,27 @@
@charset 'UTF-8';
-@keyframes highlight {
- from {
- background: rgba(yellow, .5);
- }
- to {
- background: none;
- }
-}
-
.animation--highlight {
&:before {
- content: '';
animation: highlight 1.5s ease;
+ content: '';
+ height: 100%;
+ left: 0;
position: absolute;
top: 0;
- left: 0;
width: 100%;
- height: 100%;
}
}
.activity-log__table {
+ border-top: 1px solid $light-gray;
display: table;
- width: 100%;
line-height: 1.8;
- border-top: 1px solid #ddd;
padding: 15px 0;
+ width: 100%;
&:first-child {
- padding-top: 0;
border: none;
+ padding-top: 0;
}
> div {
@@ -49,6 +40,7 @@
.report__platform {
font-size: 15px;
font-weight: 600;
+
.fa {
margin-right: 6px;
}
diff --git a/webapp/sass/routes/_admin-console.scss b/webapp/sass/routes/_admin-console.scss
index f83ce487e..63cf8eb13 100644
--- a/webapp/sass/routes/_admin-console.scss
+++ b/webapp/sass/routes/_admin-console.scss
@@ -15,26 +15,25 @@
}
h3 {
- font-weight: 600;
border-bottom: 1px solid #ddd;
- padding-bottom: .5em;
+ font-weight: 600;
margin: 1em 0;
+ padding-bottom: .5em;
}
.table {
- background: #fff;
+ background: $white;
}
.form-control {
- background-color: #fff;
+ background-color: $white;
border: 1px solid #ccc;
color: #555;
&:focus {
+ @include box-shadow(inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6));
border-color: #66afe9;
outline: 0;
- -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
- box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
}
}
@@ -42,7 +41,7 @@
color: #333;
&.btn-primary {
- background: #2389d7;
+ background: $primary-color;
&:hover,
&:focus,
@@ -53,7 +52,7 @@
}
.dropdown-menu {
- background: #fff;
+ background: $white;
border: 1px solid rgba(0,0,0,.15);
color: inherit;
}
@@ -86,38 +85,38 @@
> h4 {
background: #333;
- padding: 10px 10px;
margin: 1px 0 0;
+ padding: 10px;
.menu-icon--right {
- top: 6px;
right: 12px;
+ top: 6px;
}
}
}
.menu-icon--right {
- position: absolute;
- right: 10px;
- top: 3px;
font-size: 18px;
font-weight: 600;
- width: 20px;
height: 20px;
line-height: 20px;
+ position: absolute;
+ right: 10px;
text-align: center;
+ top: 3px;
+ width: 20px;
.fa {
+ color: $white;
font-size: 13px;
- right: -2px;
position: relative;
- color: #fff;
+ right: -2px;
}
}
&.nav__sub-menu {
+ @include font-smoothing(initial);
background: #111;
- -webkit-font-smoothing: auto;
&.padded {
padding: 5px 0;
@@ -125,30 +124,31 @@
li {
> a {
- font-size: 13px;
- padding: 5px 35px 5px 15px;
background: transparent;
color: #bbb;
+ font-size: 13px;
+ padding: 5px 35px 5px 15px;
&:hover {
- color: lighten($color--primary, 10);
+ color: lighten($primary-color, 10);
}
&.active {
- color: #fff;
+ color: $white;
font-weight: 600;
}
}
.nav-more {
- font-size: 13px;
- padding: 5px 15px;
background: transparent;
color: #bbb;
- display: block;
cursor: pointer;
+ display: block;
+ font-size: 13px;
+ padding: 5px 15px;
+
&:hover {
- color: lighten($color--primary, 10);
+ color: lighten($primary-color, 10);
}
}
}
@@ -166,23 +166,23 @@
}
.log__panel {
- overflow: scroll;
- width: 100%;
- height: 800px;
+ background-color: white;
border: 1px solid #ddd;
+ height: 800px;
margin-top: 10px;
+ overflow: scroll;
padding: 5px;
- background-color: white;
+ width: 100%;
}
.app__content {
color: #333;
&.admin {
- overflow: auto;
background-color: #f1f1f1;
- padding: 0 20px 20px;
min-height: 600px;
+ overflow: auto;
+ padding: 0 20px 20px;
}
.wrapper--fixed {
@@ -193,9 +193,9 @@
margin-top: 40px;
.control-label {
- text-align: left;
- padding-right: 0;
font-weight: 600;
+ padding-right: 0;
+ text-align: left;
}
.form-group {
@@ -203,22 +203,25 @@
}
.file__upload {
- position: relative;
- margin: 0 10px 10px 0;
display: inline-block;
+ margin: 0 10px 10px 0;
+ position: relative;
input {
- position: absolute;
@include opacity(0);
- width: 100%;
height: 100%;
- z-index: 5;
- top: 0;
left: 0;
+ position: absolute;
+ top: 0;
+ width: 100%;
+ z-index: 5;
}
}
.help-text {
+ color: #777;
+ margin: 10px 0 0 15px;
+
&.no-margin {
margin: 0;
}
@@ -228,9 +231,6 @@
padding-left: 23px;
}
- margin: 10px 0 0 15px;
- color: #777;
-
.help-link {
margin-right: 5px;
}
@@ -242,10 +242,10 @@
.alert {
display: inline-block;
- padding: 5px 7px;
margin: 1em 0 0;
- top: 1px;
+ padding: 5px 7px;
position: relative;
+ top: 1px;
.fa {
margin-right: 5px;
@@ -254,11 +254,11 @@
}
.banner {
- background: #fff;
+ background: $white;
border: 1px solid #ddd;
- padding: .7em 1.5em;
font-size: .95em;
margin: 2em 0;
+ padding: .7em 1.5em;
.banner__heading {
font-size: 1.5em;
@@ -275,19 +275,19 @@
.popover {
border-radius: 3px;
- width: 100%;
font-size: .95em;
+ width: 100%;
}
.panel {
- border: none;
background-color: transparent;
+ border: none;
}
.panel-default {
> .panel-heading {
- padding: 10px 0;
background-color: transparent;
+ padding: 10px 0;
}
.panel-body {
@@ -304,9 +304,9 @@
line-height: 1.5;
a {
- text-decoration: none;
- display: block;
@include clearfix;
+ display: block;
+ text-decoration: none;
&.collapsed {
.fa-minus {
@@ -319,10 +319,10 @@
}
.fa {
- font-size: 18px;
+ color: #aaa;
float: right;
+ font-size: 18px;
margin-top: 8px;
- color: #aaa;
}
.fa-plus {
diff --git a/webapp/sass/routes/_docs.scss b/webapp/sass/routes/_docs.scss
index 24e96772b..9a205f8a2 100644
--- a/webapp/sass/routes/_docs.scss
+++ b/webapp/sass/routes/_docs.scss
@@ -5,15 +5,15 @@
padding-bottom: 20px;
> div {
- width: 1170px;
margin: 0 auto;
- padding: 0 15px;
max-width: 100%;
+ padding: 0 15px;
+ width: 1170px;
}
h1.markdown__heading {
border-bottom: 1px solid #ddd;
- padding-bottom: 1rem;
margin: 1em 0 1em;
+ padding-bottom: 1rem;
}
}
diff --git a/webapp/sass/routes/_error-page.scss b/webapp/sass/routes/_error-page.scss
index 8cd0716ca..438e60554 100644
--- a/webapp/sass/routes/_error-page.scss
+++ b/webapp/sass/routes/_error-page.scss
@@ -8,23 +8,23 @@ body {
}
.error__container {
- max-width: 800px;
- margin: 0 auto;
+ color: #555;
display: table-cell;
- vertical-align: top;
+ margin: 0 auto;
+ max-width: 800px;
padding: 5em 0;
text-align: left;
- color: #555;
+ vertical-align: top;
}
.error__icon {
- font-size: 4em;
color: #ccc;
+ font-size: 4em;
}
h2 {
- font-weight: 600;
font-size: 1.5em;
+ font-weight: 600;
margin: .8em 0 .5em;
}
diff --git a/webapp/sass/routes/_loading.scss b/webapp/sass/routes/_loading.scss
index 8d6552957..39f37b1b3 100644
--- a/webapp/sass/routes/_loading.scss
+++ b/webapp/sass/routes/_loading.scss
@@ -4,32 +4,32 @@
.loading-screen {
display: table;
- width: 100%;
height: 100%;
padding: 60px;
text-align: center;
+ width: 100%;
.loading__content {
display: table-cell;
- vertical-align: middle;
font-size: 0;
+ vertical-align: middle;
h3 {
+ display: inline-block;
font-size: 16px;
font-weight: 400;
- margin: 0 .2em 0;
- display: inline-block;
+ margin: 0 .2em;
}
.round {
- background-color: #444;
- width: 4px;
- height: 4px;
+ @include animation(move .75s infinite linear);
+ @include border-radius(10px);
+ background-color: #444444;
display: inline-block;
+ height: 4px;
margin: 0 2px;
opacity: .1;
- @include border-radius(10px);
- @include animation(move .75s infinite linear);
+ width: 4px;
}
@for $i from 1 through 3 {
@@ -42,6 +42,7 @@
from {
opacity: 1;
}
+
to {
opacity: .1;
}
diff --git a/webapp/sass/routes/_settings.scss b/webapp/sass/routes/_settings.scss
index 3c735be3c..1c3f2e308 100644
--- a/webapp/sass/routes/_settings.scss
+++ b/webapp/sass/routes/_settings.scss
@@ -2,41 +2,47 @@
.user-settings {
min-height: 300px;
+
.table-responsive {
- max-width: 560px;
max-height: 300px;
+ max-width: 560px;
}
}
.modal {
.settings-modal {
width: 800px;
+
.modal-back {
- width: 50px;
- height: 40px;
- top: 12px;
font-size: 27px;
font-weight: normal;
+ height: 40px;
+ left: 0;
line-height: 32px;
position: absolute;
- left: 0;
text-align: center;
+ top: 12px;
+ width: 50px;
+
.fa {
+ height: 100%;
+ left: 0;
line-height: inherit;
position: absolute;
width: 100%;
- height: 100%;
- left: 0;
}
}
+
.modal-body {
- padding: 0;
margin: 0 auto;
min-height: calc(100% - 62px);
+ padding: 0;
}
+
li {
list-style: none;
}
+
label {
font-weight: 600;
}
@@ -44,87 +50,110 @@
.no-padding--left {
padding-left: 0;
}
+
.padding-top {
padding-top: 7px;
+
&.x2 {
padding-top: 14px;
}
+
&.x3 {
padding-top: 21px;
}
}
+
.padding-bottom {
padding-bottom: 7px;
+
&.x2 {
padding-bottom: 14px;
}
+
&.x3 {
padding-bottom: 21px;
}
+
.control-label {
font-weight: 600;
+
&.text-left {
text-align: left;
}
}
}
+
.profile-img {
- width: 128px;
height: 128px;
+ width: 128px;
}
+
.settings-table {
- max-width: 1000px;
- margin: 0 auto;
display: table;
+ margin: 0 auto;
+ max-width: 1000px;
table-layout: fixed;
width: 100%;
+
> div {
display: table-cell;
vertical-align: top;
}
+
.nav {
position: fixed;
width: 179px;
+
&.position--top {
top: 57px;
}
}
+
.security-links {
margin-right: 20px;
+
.fa {
margin-right: 6px;
}
}
+
.settings-links {
width: 180px;
}
+
.settings-content {
- padding: 0px 20px 30px;
+ padding: 0 20px 30px;
+
.modal-header {
display: none;
}
+
.section-min {
padding: 1em 0;
margin-bottom: 0;
cursor: pointer;
position: relative;
@include clearfix;
+
&:hover {
background: #f9f9f9;
}
+
&:hover .fa {
display: inline-block;
}
+
&:hover .section-edit {
text-decoration: underline;
}
}
.section-max {
- background: rgba(black, .05);
- padding: 1em 0 1.3em;
- margin-bottom: 0;
@include clearfix;
+ @include alpha-property(background, $black, .05);
+ margin-bottom: 0;
+ padding: 1em 0 1.3em;
+
.section-title {
margin-bottom: 10px;
}
@@ -136,50 +165,54 @@
padding: 4px 5px;
width: 40px;
}
+
img {
border: 1px solid rgba(black, .15);
width: 29px;
}
}
+
.group--code {
+
select {
+ @include appearance(none);
+ appearance: none;
padding-right: 25px;
+
+ &::ms-expand {
+ display: none;
+ }
}
- select::-ms-expand {
- display: none;
- }
+
&:before {
- pointer-events: none;
- position: absolute;
- top: 11px;
- right: 50px;
- z-index: 5;
+ @include font-smoothing;
content: '\f0d7';
display: inline-block;
font: normal normal normal 14px/1 FontAwesome;
- font-size: inherit;
+ pointer-events: none;
+ position: absolute;
+ right: 50px;
text-rendering: auto;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- }
- select {
- -moz-appearance: none;
- -webkit-appearance: none;
- appearance: none;
+ top: 11px;
+ z-index: 5;
}
}
+
.premade-themes {
margin-bottom: 10px;
+
.theme-label {
font-weight: 400;
margin-top: 5px;
}
+
img {
border: 3px solid transparent;
}
+
.active {
img {
- border-color: $color--primary;
+ border-color: $primary-color;
}
}
}
@@ -197,12 +230,12 @@
}
.theme-elements__header {
- margin: 10px 20px 0px 0;
border-bottom: 1px solid #ccc;
- padding: 5px 0 10px;
+ cursor: pointer;
font-size: em(13.5px);
font-weight: 600;
- cursor: pointer;
+ margin: 10px 20px 0 0;
+ padding: 5px 0 10px;
.fa-minus {
display: none;
@@ -212,39 +245,41 @@
.fa-minus {
display: inline-block;
}
+
.fa-plus {
display: none;
}
}
.header__icon {
- float: right;
@include opacity(.5);
+ float: right;
}
}
.theme-elements__body {
- padding-top: 5px;
- display: none;
+ @include border-radius(0 0 3px 3px);
@include legacy-pie-clearfix;
- background: rgba(255, 255, 255, .05);
- padding: 20px 0 0 20px;
+ @include alpha-property(background-color, $white, .05);
+ display: none;
margin: 0 20px 0 0;
- @include border-radius(0 0 3px 3px);
+ padding: 20px 0 0 20px;
}
.custom-label {
- white-space: nowrap;
- font-weight: normal;
font-size: 12px;
- width: 100%;
+ font-weight: normal;
+ margin-bottom: 0;
overflow: hidden;
text-overflow: ellipsis;
- margin-bottom: 0;
+ white-space: nowrap;
+ width: 100%;
}
+
.input-group-addon {
background: transparent;
}
+
.radio {
label {
font-weight: 600;
@@ -253,32 +288,33 @@
}
.section-title {
- margin-bottom: 5px;
font-weight: 600;
+ margin-bottom: 5px;
padding-right: 50px;
}
.section-edit {
- text-align: right;
margin-bottom: 5px;
+ text-align: right;
+
.fa {
- margin-right: 5px;
- font-size: 12px;
@include opacity(.5);
display: none;
+ font-size: 12px;
+ margin-right: 5px;
&.fa-chevron-down {
- top: -1px;
margin-right: 0;
position: relative;
+ top: -1px;
}
}
}
.section-describe {
- @include opacity(.7);
- white-space: pre;
@include clearfix;
+ @include opacity(.7);
text-overflow: ellipsis;
+ white-space: pre;
}
.divider-dark {
@@ -290,8 +326,8 @@
}
.setting-list {
- padding: 0;
list-style-type: none;
+ padding: 0;
}
.setting-list__hint {
@@ -305,68 +341,77 @@
.setting-list-item {
margin-top: 7px;
}
+
.has-error {
color: #a94442;
}
.file-status {
+ color: #555;
font-size: 13px;
margin-top: 8px;
- color: #555;
}
.confirm-import {
- padding: 4px 10px;
margin: 10px 0;
+ padding: 4px 10px;
}
}
}
}
+
.nav-pills {
> li {
margin: 0;
+
a {
- padding: 8px 5px 8px 15px;
border-radius: 0;
color: #777;
+ padding: 8px 5px 8px 15px;
}
+
.glyphicon {
- width: 25px;
top: 2px;
+ width: 25px;
}
+
&:hover {
a {
- background-color: rgba(black, .1);
+ @include alpha-property(background-color, $black, .1);
}
}
+
&.active {
a {
- color: #111;
background-color: #e1e1e1;
+ color: #111;
+
&:before {
+ background: $black;
content: '';
+ height: 100%;
+ left: 0;
position: absolute;
top: 0;
- left: 0;
width: 5px;
- height: 100%;
- background: #000;
}
}
+
a,
a:hover,
a:focus {
- background-color: rgba(black, .1);
+ @include alpha-property(background-color, $black, .1);
border-radius: 0;
font-weight: 400;
- position: relative;
- white-space: nowrap;
overflow: hidden;
+ position: relative;
text-overflow: ellipsis;
+ white-space: nowrap;
}
}
}
}
+
h3 {
font-size: em(20px);
}
@@ -391,13 +436,15 @@
.member-div {
border-bottom: 1px solid lightgrey;
- position: relative;
- padding: 2px;
margin: 0;
+ padding: 2px;
+ position: relative;
width: 100%;
+
&:first-child {
border-top: 1px solid lightgrey;
}
+
.post-profile-img {
@include border-radius(50px);
margin-right: 8px;
@@ -407,14 +454,14 @@
.member-role,
.member-drop {
.fa {
- margin-right: 5px;
@include opacity(.5);
font-size: 12px;
+ margin-right: 5px;
}
.member-menu {
- top: -50%;
right: 110%;
+ top: -50%;
}
}
@@ -425,13 +472,13 @@
}
.member-menu {
- right: 0px;
left: auto;
+ right: 0;
}
.member-list {
- width: 100%;
overflow-x: visible;
+ width: 100%;
}
.member-page {
diff --git a/webapp/sass/routes/_signup.scss b/webapp/sass/routes/_signup.scss
index 53c59c222..09f8e4185 100644
--- a/webapp/sass/routes/_signup.scss
+++ b/webapp/sass/routes/_signup.scss
@@ -1,19 +1,19 @@
@charset 'UTF-8';
.signup-header {
- width: 100%;
+ background: $bg--gray;
line-height: 33px;
padding: 0 1em .2em;
- background: $bg--gray;
+ width: 100%;
.fa {
margin-right: 5px;
}
}
.signup-team__container {
- padding: 100px 0px 50px 0px;
- max-width: 380px;
margin: 0 auto;
+ max-width: 380px;
+ padding: 100px 0 50px;
position: relative;
&.padding--less {
@@ -21,7 +21,7 @@
}
.form-control:focus {
- @include box-shadow(inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6));
+ @include box-shadow(inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6));
}
h1,
@@ -39,16 +39,16 @@
}
h2 {
+ font-size: em(30px);
font-weight: 600;
- margin-bottom: .8em;
letter-spacing: -.5px;
- font-size: em(30px);
+ margin-bottom: .8em;
}
h3 {
- font-weight: 600;
- margin: 0 0 1.3em 0;
font-size: 1.5em;
+ font-weight: 600;
+ margin: 0 0 1.3em;
}
h4 {
@@ -66,7 +66,7 @@
}
p {
- color: #555;
+ color: #555555;
line-height: 1.5;
margin-bottom: 1em;
}
@@ -81,32 +81,32 @@
}
.form-control {
- text-align: left;
display: table-cell;
+ text-align: left;
width: 100%;
}
.input-group-addon {
- text-align: left;
- width: 50%;
display: table-cell;
overflow: hidden;
+ text-align: left;
text-overflow: ellipsis;
+ width: 50%;
}
}
}
.inner__content {
- padding: 0 1rem;
margin: 30px 0 20px;
+ padding: 0 1rem;
}
.block--gray {
+ @include border-radius(3px);
background: #f2f2f2;
display: inline-block;
- padding: .85em 1.2em;
font-weight: 600;
- @include border-radius(3px);
+ padding: .85em 1.2em;
}
form {
@@ -114,32 +114,32 @@
}
.signup-team-confirm__container {
- padding: 100px 0px 100px 0px;
+ padding: 100px 0;
}
.signup-team-logo {
display: none;
+ margin: 0 0 2em;
width: 210px;
- margin: 0 0 2em 0;
}
.signup-team-login {
- padding-bottom: 10px;
font-weight: 600;
+ padding-bottom: 10px;
}
.signup-team__name {
- margin: .5em 0 0;
font-size: 2.2em;
font-weight: 600;
+ margin: .5em 0 0;
padding-left: 1rem;
}
.signup-team__subdomain {
- margin: .2em 0 1.2em;
font-size: 1.5em;
- padding-left: 1rem;
font-weight: 300;
+ margin: .2em 0 1.2em;
+ padding-left: 1rem;
text-transform: uppercase;
}
@@ -148,21 +148,21 @@
}
.or__container {
+ background: #dddddd;
height: 1px;
- background: #ddd;
- text-align: left;
margin: 2em 0;
+ text-align: left;
span {
- width: 40px;
- top: -10px;
- position: relative;
+ background: #ffffff;
+ display: inline-block;
font-size: 1.14286em;
- line-height: 20px;
font-weight: 600;
- background: #fff;
- display: inline-block;
+ line-height: 20px;
+ position: relative;
text-align: center;
+ top: -10px;
+ width: 40px;
}
}
@@ -177,9 +177,9 @@
.btn {
font-size: 1em;
- padding: em(7px) em(15px);
font-weight: 600;
margin-right: 5px;
+ padding: em(7px) em(15px);
.fa {
font-size: 17px;
@@ -187,29 +187,29 @@
}
.icon {
- width: 18px;
- height: 18px;
- margin-right: 8px;
@include background-size(100% 100%);
display: inline-block;
+ height: 18px;
+ margin-right: 8px;
+ width: 18px;
}
&.btn-custom-login {
+ @include border-radius(2px);
+ color: $white;
display: block;
- min-width: 200px;
- width: 200px;
- padding: 0 1em;
- margin: 1em 1rem;
height: 40px;
line-height: 34px;
- color: #fff;
- @include border-radius(2px);
+ margin: 1em 1rem;
+ min-width: 200px;
+ padding: 0 1em;
+ width: 200px;
&.gitlab {
- background: #548;
+ background: #554488;
&:hover {
- background: darken(#548, 10%);
+ background: darken(#554488, 10%);
}
span {
@@ -250,10 +250,10 @@
}
&.email {
- background: #2389d7;
+ background: $primary-color;
&:hover {
- background: darken(#2389d7, 10%);
+ background: $primary-color--hover;
}
span {
@@ -269,15 +269,15 @@
}
&.btn-default {
- color: #444;
+ color: #444444;
}
.glyphicon {
&.glyphicon-ok,
&.glyphicon-refresh {
- margin-right: .5em;
- left: -2px;
font-size: .9em;
+ left: -2px;
+ margin-right: .5em;
}
&.glyphicon-chevron-right {
@@ -296,28 +296,29 @@
.has-error {
.control-label {
- background: #f2f2f2;
- padding: .7em 1em;
@include border-radius(3px);
- margin: 1em 0 0;
+ background: #f2f2f2;
+ color: #999999;
font-size: 14px;
font-weight: normal;
- color: #999;
+ margin: 1em 0 0;
+ padding: .7em 1em;
width: 100%;
&:before {
- @extend .fa;
+ @extend %font-awesome;
+ color: #aaaaaa;
content: '\f071';
margin-right: 4px;
- color: #aaa;
}
}
}
.reset-form {
@include border-radius(3px);
- position: relative;
font-size: .95em;
+ position: relative;
+
p {
color: inherit;
}
@@ -363,12 +364,12 @@
.black,
&.black {
- color: #000;
+ color: #000000;
}
}
.color--light {
- color: #777;
+ color: #777777;
}
.margin--extra {
@@ -381,9 +382,10 @@
}
.signup-team-all {
- margin: 0 0 20px;
- border: 1px solid #ddd;
@include border-radius(2px);
+ border: 1px solid #dddddd;
+ margin: 0 0 20px;
+
.signup-team-dir {
background: #fafafa;
border-top: 1px solid #d5d5d5;
@@ -395,25 +397,25 @@
a {
color: inherit;
display: block;
- padding: 0 15px;
- line-height: 3.5em;
- height: 3.5em;
font-size: 1.1em;
+ height: 3.5em;
+ line-height: 3.5em;
+ padding: 0 15px;
}
}
.signup-team-dir__name {
- white-space: nowrap;
float: left;
overflow: hidden;
text-overflow: ellipsis;
+ white-space: nowrap;
width: 90%;
}
.signup-team-dir__arrow {
+ color: #999999;
float: right;
font-size: .9em;
- color: #999;
line-height: 3.5em;
}
}
diff --git a/webapp/sass/routes/_statistics.scss b/webapp/sass/routes/_statistics.scss
index b48c137c3..797bc480b 100644
--- a/webapp/sass/routes/_statistics.scss
+++ b/webapp/sass/routes/_statistics.scss
@@ -2,45 +2,45 @@
.team_statistics {
.total-count {
+ @include border-radius(3px);
+ background: $white;
+ border: 1px solid $light-gray;
margin: 1em 0;
text-align: center;
- background: #fff;
- border: 1px solid #ddd;
width: 100%;
- @include border-radius(3px);
.title {
+ border-bottom: 1px solid $light-gray;
+ font-size: 13px;
font-weight: 400;
padding: 7px 10px;
- border-bottom: 1px solid #ddd;
text-align: left;
- font-size: 13px;
.fa {
+ color: #555555;
float: right;
- margin: 0px 0 0;
- color: #555;
font-size: 16px;
+ margin: 0;
}
}
.content {
+ color: #555555;
font-size: 30px;
font-weight: 600;
- color: #555;
- padding: .3em 0 .35em;
overflow: auto;
+ padding: .3em 0 .35em;
}
}
.total-count--day {
- width: 760px;
- height: 275px;
- border: 1px solid #ddd;
- padding: 5px 10px 10px 10px;
- margin: 10px 10px 10px 10px;
- background: #fff;
+ background: $white;
+ border: 1px solid $light-gray;
clear: both;
+ height: 275px;
+ margin: 10px;
+ padding: 5px 10px 10px;
+ width: 760px;
> div {
font-size: 18px;
@@ -50,8 +50,8 @@
.recent-active-users {
table {
- width: 100%;
table-layout: fixed;
+ width: 100%;
}
.content {
@@ -68,14 +68,15 @@
}
td {
- font-weight: 400;
- white-space: nowrap;
- text-overflow: ellipsis;
+ @include clearfix;
+ border-left: 1px solid $light-gray;
+ border-top: 1px solid $light-gray;
font-size: 13px;
- border-left: 1px solid #ddd;
- border-top: 1px solid #ddd;
+ font-weight: 400;
padding: 5px 5px 6px;
- @include clearfix;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+
&:first-child {
border-left: none;
}
diff --git a/webapp/sass/utils/_animations.scss b/webapp/sass/utils/_animations.scss
index 767e30847..ffdbcb219 100644
--- a/webapp/sass/utils/_animations.scss
+++ b/webapp/sass/utils/_animations.scss
@@ -1,19 +1,21 @@
@charset 'UTF-8';
-@-webkit-keyframes spin2 {
+@keyframes spin {
from {
- -webkit-transform: rotate(0deg);
+ transform: scale(1) rotate(0deg);
}
+
to {
- -webkit-transform: rotate(360deg);
+ transform: scale(1) rotate(360deg);
}
}
-@keyframes spin {
+@keyframes highlight {
from {
- transform: scale(1) rotate(0deg);
+ @include alpha-property(background, $yellow, .5);
}
+
to {
- transform: scale(1) rotate(360deg);
+ background: none;
}
}
diff --git a/webapp/sass/utils/_functions.scss b/webapp/sass/utils/_functions.scss
index 9013920fa..baedd72c5 100644
--- a/webapp/sass/utils/_functions.scss
+++ b/webapp/sass/utils/_functions.scss
@@ -4,6 +4,20 @@
@return #{$pixels/$context}em
}
+@function alpha-color($color, $amount) {
+ @return rgba($color, $amount)
+}
+
%popover-box-shadow {
@include box-shadow(rgba(black, .175) 1px -3px 12px);
+}
+
+%font-awesome {
+ display: inline-block;
+ font: normal normal normal 14px/1 FontAwesome;
+ font-size: inherit;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ transform: translate(0, 0);
} \ No newline at end of file
diff --git a/webapp/sass/utils/_mixins.scss b/webapp/sass/utils/_mixins.scss
index 28723e1a5..6e4488fca 100644
--- a/webapp/sass/utils/_mixins.scss
+++ b/webapp/sass/utils/_mixins.scss
@@ -1,8 +1,29 @@
@charset 'UTF-8';
@mixin file-icon($path) {
- background: #fff url($path);
+ @include background-size(60px auto);
+ background-color: $white;
+ background-image: url($path);
background-position: center;
background-repeat: no-repeat;
- @include background-size(60px auto);
}
+
+@mixin alpha-property($property, $color, $opacity) {
+ #{$property}: rgba($color, $opacity);
+}
+
+@mixin font-smoothing($value: antialiased) {
+ @if $value == antialiased {
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ }
+ @else {
+ -webkit-font-smoothing: subpixel-antialiased;
+ -moz-osx-font-smoothing: auto;
+ }
+}
+
+@mixin cursor($value) {
+ cursor: -webkit-$value;
+ cursor: zoom-$value;
+} \ No newline at end of file
diff --git a/webapp/sass/utils/_module.scss b/webapp/sass/utils/_module.scss
index e7f0c0c46..5dddcaef7 100644
--- a/webapp/sass/utils/_module.scss
+++ b/webapp/sass/utils/_module.scss
@@ -1,5 +1,5 @@
// Only for combining all the files in this folder
@import 'variables';
-@import 'animations';
@import 'functions';
@import 'mixins';
+@import 'animations';
diff --git a/webapp/sass/utils/_variables.scss b/webapp/sass/utils/_variables.scss
index 065c37a17..345ab11e8 100644
--- a/webapp/sass/utils/_variables.scss
+++ b/webapp/sass/utils/_variables.scss
@@ -1,10 +1,16 @@
@charset 'UTF-8';
// Color Variables
-$color--primary: rgb(35, 137, 215);
-$color--primary--hover: darken($color--primary, 10%);
+$primary-color: rgb(35, 137, 215);
+$primary-color--hover: darken($primary-color, 10%);
$bg--gray: rgb(245, 245, 245);
-$bg--white: rgb(255, 255, 255);
+$white: rgb(255, 255, 255);
+$black: rgb(0, 0, 0);
+$red: rgb(229, 101, 101);
+$yellow: rgb(255, 255, 0);
+$light-gray: rgba(0, 0, 0, .06);
+$gray: rgba(0, 0, 0, .3);
+$dark-gray: rgba(0, 0, 0, .5);
// Page Variables
$border-gray: 1px solid #ddd;
diff --git a/webapp/utils/async_client.jsx b/webapp/utils/async_client.jsx
index 9c40311cf..9a5869f9a 100644
--- a/webapp/utils/async_client.jsx
+++ b/webapp/utils/async_client.jsx
@@ -755,12 +755,12 @@ export function savePreferences(preferences, success, error) {
);
}
-export function getSuggestedCommands(command, suggestionId, component) {
- client.listCommands(
+export function getSuggestedCommands(command, channelId, suggestionId, component) {
+ client.listCommands({command: command, channelId: channelId},
(data) => {
var matches = [];
data.forEach((cmd) => {
- if (('/' + cmd.trigger).indexOf(command) === 0) {
+ if (('/' + cmd.trigger).indexOf(command) === 0 || cmd.external_management) {
let s = '/' + cmd.trigger;
let hint = '';
if (cmd.auto_complete_hint && cmd.auto_complete_hint.length !== 0) {
diff --git a/webapp/utils/client.jsx b/webapp/utils/client.jsx
index 9bd62e22d..ef6d496a2 100644
--- a/webapp/utils/client.jsx
+++ b/webapp/utils/client.jsx
@@ -1002,12 +1002,13 @@ export function regenCommandToken(data, success, error) {
});
}
-export function listCommands(success, error) {
+export function listCommands(data, success, error) {
$.ajax({
url: '/api/v1/commands/list',
dataType: 'json',
contentType: 'application/json',
- type: 'GET',
+ type: 'POST',
+ data: JSON.stringify(data),
success,
error: function onError(xhr, status, err) {
var e = handleError('listCommands', xhr, status, err);
diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx
index 32123e369..29178aca6 100644
--- a/webapp/utils/constants.jsx
+++ b/webapp/utils/constants.jsx
@@ -144,7 +144,7 @@ export default {
PRESENTATION_TYPES: ['ppt', 'pptx'],
SPREADSHEET_TYPES: ['xlsx', 'csv'],
WORD_TYPES: ['doc', 'docx'],
- CODE_TYPES: ['css', 'html', 'js', 'php', 'rb'],
+ CODE_TYPES: ['as', 'applescript', 'osascript', 'scpt', 'bash', 'sh', 'zsh', 'clj', 'boot', 'cl2', 'cljc', 'cljs', 'cljs.hl', 'cljscm', 'cljx', 'hic', 'coffee', '_coffee', 'cake', 'cjsx', 'cson', 'iced', 'cpp', 'c', 'cc', 'h', 'c++', 'h++', 'hpp', 'cs', 'csharp', 'css', 'd', 'di', 'dart', 'delphi', 'dpr', 'dfm', 'pas', 'pascal', 'freepascal', 'lazarus', 'lpr', 'lfm', 'diff', 'django', 'jinja', 'dockerfile', 'docker', 'erl', 'f90', 'f95', 'fsharp', 'fs', 'gcode', 'nc', 'go', 'groovy', 'handlebars', 'hbs', 'html.hbs', 'html.handlebars', 'hs', 'hx', 'java', 'jsp', 'js', 'jsx', 'json', 'jl', 'kt', 'ktm', 'kts', 'less', 'lisp', 'lua', 'mk', 'mak', 'md', 'mkdown', 'mkd', 'matlab', 'm', 'mm', 'objc', 'obj-c', 'ml', 'perl', 'pl', 'php', 'php3', 'php4', 'php5', 'php6', 'ps', 'ps1', 'pp', 'py', 'gyp', 'r', 'ruby', 'rb', 'gemspec', 'podspec', 'thor', 'irb', 'rs', 'scala', 'scm', 'sld', 'scss', 'st', 'sql', 'swift', 'tex', 'vbnet', 'vb', 'bas', 'vbs', 'v', 'veo', 'xml', 'html', 'xhtml', 'rss', 'atom', 'xsl', 'plist', 'yaml'],
PDF_TYPES: ['pdf'],
PATCH_TYPES: ['patch'],
ICON_FROM_TYPE: {
@@ -514,30 +514,64 @@ export default {
SPACE: 32,
TAB: 9
},
+ CODE_PREVIEW_MAX_FILE_SIZE: 500000, // 500 KB
HighlightedLanguages: {
- diff: 'Diff',
- apache: 'Apache',
- makefile: 'Makefile',
- http: 'HTTP',
- json: 'JSON',
- markdown: 'Markdown',
- javascript: 'JavaScript',
- css: 'CSS',
- nginx: 'nginx',
- objectivec: 'Objective-C',
- python: 'Python',
- xml: 'XML',
- perl: 'Perl',
- bash: 'Bash',
- php: 'PHP',
- coffeescript: 'CoffeeScript',
- cs: 'C#',
- cpp: 'C++',
- sql: 'SQL',
- go: 'Go',
- ruby: 'Ruby',
- java: 'Java',
- ini: 'ini'
+ actionscript: {name: 'ActionScript', extensions: ['as']},
+ applescript: {name: 'AppleScript', extensions: ['applescript', 'osascript', 'scpt']},
+ bash: {name: 'Bash', extensions: ['bash', 'sh', 'zsh']},
+ clojure: {name: 'Clojure', extensions: ['clj', 'boot', 'cl2', 'cljc', 'cljs', 'cljs.hl', 'cljscm', 'cljx', 'hic']},
+ coffeescript: {name: 'CoffeeScript', extensions: ['coffee', '_coffee', 'cake', 'cjsx', 'cson', 'iced']},
+ cpp: {name: 'C/C++', extensions: ['cpp', 'c', 'cc', 'h', 'c++', 'h++', 'hpp']},
+ cs: {name: 'C#', extensions: ['cs', 'csharp']},
+ css: {name: 'CSS', extensions: ['css']},
+ d: {name: 'D', extensions: ['d', 'di']},
+ dart: {name: 'Dart', extensions: ['dart']},
+ delphi: {name: 'Delphi', extensions: ['delphi', 'dpr', 'dfm', 'pas', 'pascal', 'freepascal', 'lazarus', 'lpr', 'lfm']},
+ diff: {name: 'Diff', extensions: ['diff', 'patch']},
+ django: {name: 'Django', extensions: ['django', 'jinja']},
+ dockerfile: {name: 'Dockerfile', extensions: ['dockerfile', 'docker']},
+ erlang: {name: 'Erlang', extensions: ['erl']},
+ fortran: {name: 'Fortran', extensions: ['f90', 'f95']},
+ fsharp: {name: 'F#', extensions: ['fsharp', 'fs']},
+ gcode: {name: 'G-Code', extensions: ['gcode', 'nc']},
+ go: {name: 'Go', extensions: ['go']},
+ groovy: {name: 'Groovy', extensions: ['groovy']},
+ handlebars: {name: 'Handlebars', extensions: ['handlebars', 'hbs', 'html.hbs', 'html.handlebars']},
+ haskell: {name: 'Haskell', extensions: ['hs']},
+ haxe: {name: 'Haxe', extensions: ['hx']},
+ java: {name: 'Java', extensions: ['java', 'jsp']},
+ javascript: {name: 'JavaScript', extensions: ['js', 'jsx']},
+ json: {name: 'JSON', extensions: ['json']},
+ julia: {name: 'Julia', extensions: ['jl']},
+ kotlin: {name: 'Kotlin', extensions: ['kt', 'ktm', 'kts']},
+ less: {name: 'Less', extensions: ['less']},
+ lisp: {name: 'Lisp', extensions: ['lisp']},
+ lua: {name: 'Lua', extensions: ['lua']},
+ makefile: {name: 'Makefile', extensions: ['mk', 'mak']},
+ markdown: {name: 'Markdown', extensions: ['md', 'mkdown', 'mkd']},
+ matlab: {name: 'Matlab', extensions: ['matlab', 'm']},
+ objectivec: {name: 'Objective C', extensions: ['mm', 'objc', 'obj-c']},
+ ocaml: {name: 'OCaml', extensions: ['ml']},
+ perl: {name: 'Perl', extensions: ['perl', 'pl']},
+ php: {name: 'PHP', extensions: ['php', 'php3', 'php4', 'php5', 'php6']},
+ powershell: {name: 'PowerShell', extensions: ['ps', 'ps1']},
+ puppet: {name: 'Puppet', extensions: ['pp']},
+ python: {name: 'Python', extensions: ['py', 'gyp']},
+ r: {name: 'R', extensions: ['r']},
+ ruby: {name: 'Ruby', extensions: ['ruby', 'rb', 'gemspec', 'podspec', 'thor', 'irb']},
+ rust: {name: 'Rust', extensions: ['rs']},
+ scala: {name: 'Scala', extensions: ['scala']},
+ scheme: {name: 'Scheme', extensions: ['scm', 'sld']},
+ scss: {name: 'SCSS', extensions: ['scss']},
+ smalltalk: {name: 'Smalltalk', extensions: ['st']},
+ sql: {name: 'SQL', extensions: ['sql']},
+ swift: {name: 'Swift', extensions: ['swift']},
+ tex: {name: 'TeX', extensions: ['tex']},
+ vbnet: {name: 'VB.Net', extensions: ['vbnet', 'vb', 'bas']},
+ vbscript: {name: 'VBScript', extensions: ['vbs']},
+ verilog: {name: 'Verilog', extensions: ['v', 'veo']},
+ xml: {name: 'HTML, XML', extensions: ['xml', 'html', 'xhtml', 'rss', 'atom', 'xsl', 'plist']},
+ yaml: {name: 'YAML', extensions: ['yaml']}
},
PostsViewJumpTypes: {
BOTTOM: 1,
@@ -560,6 +594,10 @@ export default {
EMBED_TOGGLE: {
label: 'embed_toggle',
description: 'Show toggle for all embed previews'
+ },
+ SLASHCMD_AUTOCMP: {
+ label: 'slashCmd_autocmp',
+ description: 'Enable external application to offer slash command autocomplete'
}
},
OVERLAY_TIME_DELAY: 400,
diff --git a/webapp/utils/markdown.jsx b/webapp/utils/markdown.jsx
index 635a39290..2cf1c5af0 100644
--- a/webapp/utils/markdown.jsx
+++ b/webapp/utils/markdown.jsx
@@ -1,65 +1,14 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import highlightJs from 'highlight.js/lib/highlight.js';
-import highlightJsDiff from 'highlight.js/lib/languages/diff.js';
-import highlightJsApache from 'highlight.js/lib/languages/apache.js';
-import highlightJsMakefile from 'highlight.js/lib/languages/makefile.js';
-import highlightJsHttp from 'highlight.js/lib/languages/http.js';
-import highlightJsJson from 'highlight.js/lib/languages/json.js';
-import highlightJsMarkdown from 'highlight.js/lib/languages/markdown.js';
-import highlightJsJavascript from 'highlight.js/lib/languages/javascript.js';
-import highlightJsCss from 'highlight.js/lib/languages/css.js';
-import highlightJsNginx from 'highlight.js/lib/languages/nginx.js';
-import highlightJsObjectivec from 'highlight.js/lib/languages/objectivec.js';
-import highlightJsPython from 'highlight.js/lib/languages/python.js';
-import highlightJsXml from 'highlight.js/lib/languages/xml.js';
-import highlightJsPerl from 'highlight.js/lib/languages/perl.js';
-import highlightJsBash from 'highlight.js/lib/languages/bash.js';
-import highlightJsPhp from 'highlight.js/lib/languages/php.js';
-import highlightJsCoffeescript from 'highlight.js/lib/languages/coffeescript.js';
-import highlightJsCs from 'highlight.js/lib/languages/cs.js';
-import highlightJsCpp from 'highlight.js/lib/languages/cpp.js';
-import highlightJsSql from 'highlight.js/lib/languages/sql.js';
-import highlightJsGo from 'highlight.js/lib/languages/go.js';
-import highlightJsRuby from 'highlight.js/lib/languages/ruby.js';
-import highlightJsJava from 'highlight.js/lib/languages/java.js';
-import highlightJsIni from 'highlight.js/lib/languages/ini.js';
-
-highlightJs.registerLanguage('diff', highlightJsDiff);
-highlightJs.registerLanguage('apache', highlightJsApache);
-highlightJs.registerLanguage('makefile', highlightJsMakefile);
-highlightJs.registerLanguage('http', highlightJsHttp);
-highlightJs.registerLanguage('json', highlightJsJson);
-highlightJs.registerLanguage('markdown', highlightJsMarkdown);
-highlightJs.registerLanguage('javascript', highlightJsJavascript);
-highlightJs.registerLanguage('css', highlightJsCss);
-highlightJs.registerLanguage('nginx', highlightJsNginx);
-highlightJs.registerLanguage('objectivec', highlightJsObjectivec);
-highlightJs.registerLanguage('python', highlightJsPython);
-highlightJs.registerLanguage('xml', highlightJsXml);
-highlightJs.registerLanguage('perl', highlightJsPerl);
-highlightJs.registerLanguage('bash', highlightJsBash);
-highlightJs.registerLanguage('php', highlightJsPhp);
-highlightJs.registerLanguage('coffeescript', highlightJsCoffeescript);
-highlightJs.registerLanguage('cs', highlightJsCs);
-highlightJs.registerLanguage('cpp', highlightJsCpp);
-highlightJs.registerLanguage('sql', highlightJsSql);
-highlightJs.registerLanguage('go', highlightJsGo);
-highlightJs.registerLanguage('ruby', highlightJsRuby);
-highlightJs.registerLanguage('java', highlightJsJava);
-highlightJs.registerLanguage('ini', highlightJsIni);
-
import * as TextFormatting from './text_formatting.jsx';
import * as Utils from './utils.jsx';
+import * as syntaxHightlighting from './syntax_hightlighting.jsx';
import marked from 'marked';
import katex from 'katex';
import 'katex/dist/katex.min.css';
-import Constants from 'utils/constants.jsx';
-const HighlightedLanguages = Constants.HighlightedLanguages;
-
function markdownImageLoaded(image) {
image.style.height = 'auto';
}
@@ -110,31 +59,11 @@ class MattermostMarkdownRenderer extends marked.Renderer {
this.formattingOptions = formattingOptions;
}
- code(code, language, escaped) {
+ code(code, language) {
let usedLanguage = language || '';
usedLanguage = usedLanguage.toLowerCase();
- // treat html as xml to prevent injection attacks
- if (usedLanguage === 'html') {
- usedLanguage = 'xml';
- }
-
- if (HighlightedLanguages[usedLanguage]) {
- const parsed = highlightJs.highlight(usedLanguage, code);
-
- return (
- '<div class="post-body--code">' +
- '<span class="post-body--code__language">' +
- HighlightedLanguages[usedLanguage] +
- '</span>' +
- '<pre>' +
- '<code class="hljs">' +
- parsed.value +
- '</code>' +
- '</pre>' +
- '</div>'
- );
- } else if (usedLanguage === 'tex' || usedLanguage === 'latex') {
+ if (usedLanguage === 'tex' || usedLanguage === 'latex') {
try {
const html = katex.renderToString(code, {throwOnError: false, displayMode: true});
@@ -144,13 +73,12 @@ class MattermostMarkdownRenderer extends marked.Renderer {
}
}
- return (
- '<pre>' +
- '<code class="hljs">' +
- (escaped ? code : TextFormatting.sanitizeHtml(code)) + '\n' +
- '</code>' +
- '</pre>'
- );
+ // treat html as xml to prevent injection attacks
+ if (usedLanguage === 'html') {
+ usedLanguage = 'xml';
+ }
+
+ return syntaxHightlighting.formatCode(usedLanguage, code);
}
codespan(text) {
diff --git a/webapp/utils/syntax_hightlighting.jsx b/webapp/utils/syntax_hightlighting.jsx
new file mode 100644
index 000000000..981ce6b35
--- /dev/null
+++ b/webapp/utils/syntax_hightlighting.jsx
@@ -0,0 +1,197 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import * as Utils from './utils.jsx';
+import * as TextFormatting from './text_formatting.jsx';
+import Constants from './constants.jsx';
+
+import hlJS from 'highlight.js/lib/highlight.js';
+
+import hljsActionscript from 'highlight.js/lib/languages/actionscript.js';
+import hljsApplescript from 'highlight.js/lib/languages/applescript.js';
+import hljsBash from 'highlight.js/lib/languages/bash.js';
+import hljsClojure from 'highlight.js/lib/languages/clojure.js';
+import hljsCoffeescript from 'highlight.js/lib/languages/coffeescript.js';
+import hljsCpp from 'highlight.js/lib/languages/cpp.js';
+import hljsCs from 'highlight.js/lib/languages/cs.js';
+import hljsCss from 'highlight.js/lib/languages/css.js';
+import hljsD from 'highlight.js/lib/languages/d.js';
+import hljsDart from 'highlight.js/lib/languages/dart.js';
+import hljsDelphi from 'highlight.js/lib/languages/delphi.js';
+import hljsDiff from 'highlight.js/lib/languages/diff.js';
+import hljsDjango from 'highlight.js/lib/languages/django.js';
+import hljsDockerfile from 'highlight.js/lib/languages/dockerfile.js';
+import hljsErlang from 'highlight.js/lib/languages/erlang.js';
+import hljsFortran from 'highlight.js/lib/languages/fortran.js';
+import hljsFsharp from 'highlight.js/lib/languages/fsharp.js';
+import hljsGcode from 'highlight.js/lib/languages/gcode.js';
+import hljsGo from 'highlight.js/lib/languages/go.js';
+import hljsGroovy from 'highlight.js/lib/languages/groovy.js';
+import hljsHandlebars from 'highlight.js/lib/languages/handlebars.js';
+import hljsHaskell from 'highlight.js/lib/languages/haskell.js';
+import hljsHaxe from 'highlight.js/lib/languages/haxe.js';
+import hljsJava from 'highlight.js/lib/languages/java.js';
+import hljsJavascript from 'highlight.js/lib/languages/javascript.js';
+import hljsJson from 'highlight.js/lib/languages/json.js';
+import hljsJulia from 'highlight.js/lib/languages/julia.js';
+import hljsKotlin from 'highlight.js/lib/languages/kotlin.js';
+import hljsLess from 'highlight.js/lib/languages/less.js';
+import hljsLisp from 'highlight.js/lib/languages/lisp.js';
+import hljsLua from 'highlight.js/lib/languages/lua.js';
+import hljsMakefile from 'highlight.js/lib/languages/makefile.js';
+import hljsMarkdown from 'highlight.js/lib/languages/markdown.js';
+import hljsMatlab from 'highlight.js/lib/languages/matlab.js';
+import hljsObjectivec from 'highlight.js/lib/languages/objectivec.js';
+import hljsOcaml from 'highlight.js/lib/languages/ocaml.js';
+import hljsPerl from 'highlight.js/lib/languages/perl.js';
+import hljsPhp from 'highlight.js/lib/languages/php.js';
+import hljsPowershell from 'highlight.js/lib/languages/powershell.js';
+import hljsPuppet from 'highlight.js/lib/languages/puppet.js';
+import hljsPython from 'highlight.js/lib/languages/python.js';
+import hljsR from 'highlight.js/lib/languages/r.js';
+import hljsRuby from 'highlight.js/lib/languages/ruby.js';
+import hljsRust from 'highlight.js/lib/languages/rust.js';
+import hljsScala from 'highlight.js/lib/languages/scala.js';
+import hljsScheme from 'highlight.js/lib/languages/scheme.js';
+import hljsScss from 'highlight.js/lib/languages/scss.js';
+import hljsSmalltalk from 'highlight.js/lib/languages/smalltalk.js';
+import hljsSql from 'highlight.js/lib/languages/sql.js';
+import hljsSwift from 'highlight.js/lib/languages/swift.js';
+import hljsTex from 'highlight.js/lib/languages/tex.js';
+import hljsVbnet from 'highlight.js/lib/languages/vbnet.js';
+import hljsVbscript from 'highlight.js/lib/languages/vbscript.js';
+import hljsVerilog from 'highlight.js/lib/languages/verilog.js';
+import hljsXml from 'highlight.js/lib/languages/xml.js';
+import hljsYaml from 'highlight.js/lib/languages/yaml.js';
+
+hlJS.registerLanguage('actionscript', hljsActionscript);
+hlJS.registerLanguage('applescript', hljsApplescript);
+hlJS.registerLanguage('bash', hljsBash);
+hlJS.registerLanguage('clojure', hljsClojure);
+hlJS.registerLanguage('coffeescript', hljsCoffeescript);
+hlJS.registerLanguage('cpp', hljsCpp);
+hlJS.registerLanguage('cs', hljsCs);
+hlJS.registerLanguage('css', hljsCss);
+hlJS.registerLanguage('d', hljsD);
+hlJS.registerLanguage('dart', hljsDart);
+hlJS.registerLanguage('delphi', hljsDelphi);
+hlJS.registerLanguage('diff', hljsDiff);
+hlJS.registerLanguage('django', hljsDjango);
+hlJS.registerLanguage('dockerfile', hljsDockerfile);
+hlJS.registerLanguage('erlang', hljsErlang);
+hlJS.registerLanguage('fortran', hljsFortran);
+hlJS.registerLanguage('fsharp', hljsFsharp);
+hlJS.registerLanguage('gcode', hljsGcode);
+hlJS.registerLanguage('go', hljsGo);
+hlJS.registerLanguage('groovy', hljsGroovy);
+hlJS.registerLanguage('handlebars', hljsHandlebars);
+hlJS.registerLanguage('haskell', hljsHaskell);
+hlJS.registerLanguage('haxe', hljsHaxe);
+hlJS.registerLanguage('java', hljsJava);
+hlJS.registerLanguage('javascript', hljsJavascript);
+hlJS.registerLanguage('json', hljsJson);
+hlJS.registerLanguage('julia', hljsJulia);
+hlJS.registerLanguage('kotlin', hljsKotlin);
+hlJS.registerLanguage('less', hljsLess);
+hlJS.registerLanguage('lisp', hljsLisp);
+hlJS.registerLanguage('lua', hljsLua);
+hlJS.registerLanguage('makefile', hljsMakefile);
+hlJS.registerLanguage('markdown', hljsMarkdown);
+hlJS.registerLanguage('matlab', hljsMatlab);
+hlJS.registerLanguage('objectivec', hljsObjectivec);
+hlJS.registerLanguage('ocaml', hljsOcaml);
+hlJS.registerLanguage('perl', hljsPerl);
+hlJS.registerLanguage('php', hljsPhp);
+hlJS.registerLanguage('powershell', hljsPowershell);
+hlJS.registerLanguage('puppet', hljsPuppet);
+hlJS.registerLanguage('python', hljsPython);
+hlJS.registerLanguage('r', hljsR);
+hlJS.registerLanguage('ruby', hljsRuby);
+hlJS.registerLanguage('rust', hljsRust);
+hlJS.registerLanguage('scala', hljsScala);
+hlJS.registerLanguage('scheme', hljsScheme);
+hlJS.registerLanguage('scss', hljsScss);
+hlJS.registerLanguage('smalltalk', hljsSmalltalk);
+hlJS.registerLanguage('sql', hljsSql);
+hlJS.registerLanguage('swift', hljsSwift);
+hlJS.registerLanguage('tex', hljsTex);
+hlJS.registerLanguage('vbnet', hljsVbnet);
+hlJS.registerLanguage('vbscript', hljsVbscript);
+hlJS.registerLanguage('verilog', hljsVerilog);
+hlJS.registerLanguage('xml', hljsXml);
+hlJS.registerLanguage('yaml', hljsYaml);
+
+const HighlightedLanguages = Constants.HighlightedLanguages;
+
+export function formatCode(lang, data, filename) {
+ var language = lang || '';
+ var parsed;
+ var header = '';
+
+ language = language.toLowerCase();
+
+ if (HighlightedLanguages[language]) {
+ var name = HighlightedLanguages[language].name;
+
+ if (filename) {
+ const fname = decodeURIComponent(Utils.getFileName(filename));
+ name = fname + ' - ' + name;
+ }
+
+ header = '<span class="post-body--code__language">' + name + '</span>';
+
+ try {
+ parsed = hlJS.highlight(language, data).value;
+ } catch (e) {
+ parsed = TextFormatting.sanitizeHtml(data);
+ }
+ } else {
+ parsed = TextFormatting.sanitizeHtml(data);
+ }
+
+ const lines = data.match(/\r\n|\r|\n|$/g).length;
+ var strlines = '';
+ for (var i = 1; i <= lines; i++) {
+ if (strlines) {
+ strlines += '\n' + i;
+ } else {
+ strlines += i;
+ }
+ }
+
+ return (
+ '<div class="post-body--code">' +
+ header +
+ '<pre>' +
+ '<code class="hljs">' +
+ '<table>' +
+ '<tbody>' +
+ '<tr>' +
+ '<td class="post-body--code__lineno">' + strlines + '</td>' +
+ '<td>' +
+ parsed +
+ '</td>' +
+ '</tr>' +
+ '</tbody>' +
+ '</table>' +
+ '</code>' +
+ '</pre>' +
+ '</div>'
+ );
+}
+
+export function getLang(filename) {
+ const fileInfo = Utils.splitFileLocation(filename);
+ var ext = fileInfo.ext;
+ if (!ext) {
+ return null;
+ }
+
+ ext = ext.toLowerCase();
+ for (var key in HighlightedLanguages) {
+ if (HighlightedLanguages[key].extensions.find((x) => x === ext)) {
+ return key;
+ }
+ }
+ return null;
+}
diff --git a/webapp/utils/text_formatting.jsx b/webapp/utils/text_formatting.jsx
index 9833b995c..4c8b5e24c 100644
--- a/webapp/utils/text_formatting.jsx
+++ b/webapp/utils/text_formatting.jsx
@@ -213,6 +213,11 @@ function highlightCurrentMentions(text, tokens) {
}
for (const mention of UserStore.getCurrentMentionKeys()) {
+ // occasionally we get an empty mention which matches a bunch of empty strings
+ if (!mention) {
+ continue;
+ }
+
output = output.replace(new RegExp(`(^|\\W)(${escapeRegex(mention)})\\b`, 'gi'), replaceCurrentMentionWithToken);
}
diff --git a/webapp/utils/utils.jsx b/webapp/utils/utils.jsx
index 686630a9b..12a7ff50e 100644
--- a/webapp/utils/utils.jsx
+++ b/webapp/utils/utils.jsx
@@ -680,7 +680,7 @@ export function applyTheme(theme) {
}
if (theme.centerChannelBg) {
- changeCss('.app__content, .markdown__table, .markdown__table tbody tr, .suggestion-list__content, .modal .modal-content', 'background:' + theme.centerChannelBg, 1);
+ changeCss('.app__content, .markdown__table, .markdown__table tbody tr, .suggestion-list__content, .modal .modal-content, .modal .modal-back', 'background:' + theme.centerChannelBg, 1);
changeCss('#post-list .post-list-holder-by-time', 'background:' + theme.centerChannelBg, 1);
changeCss('#post-create', 'background:' + theme.centerChannelBg, 1);
changeCss('.date-separator .separator__text, .new-separator .separator__text', 'background:' + theme.centerChannelBg, 1);
@@ -763,7 +763,6 @@ export function applyTheme(theme) {
if (theme.buttonBg) {
changeCss('.btn.btn-primary, .tutorial__circles .circle.active', 'background:' + theme.buttonBg, 1);
changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background:' + changeColor(theme.buttonBg, -0.25), 1);
- changeCss('.file-playback__controls', 'color:' + changeColor(theme.buttonBg, -0.25), 1);
}
if (theme.buttonColor) {
diff --git a/webapp/webpack.config.js b/webapp/webpack.config.js
index 14abf6ffa..5e1df9bfe 100644
--- a/webapp/webpack.config.js
+++ b/webapp/webpack.config.js
@@ -17,20 +17,21 @@ module.exports = {
loaders: [
{
test: /\.jsx?$/,
- loader: 'babel-loader',
+ loader: 'babel',
exclude: /(node_modules|non_npm_dependencies)/,
query: {
- presets: ['react', 'es2015', 'stage-0'],
- plugins: ['transform-runtime']
+ presets: ['react', 'es2015-webpack', 'stage-0'],
+ plugins: ['transform-runtime'],
+ cacheDirectory: true
}
},
{
test: /\.json$/,
- loader: 'json-loader'
+ loader: 'json'
},
{
test: /(node_modules|non_npm_dependencies)\/.+\.(js|jsx)$/,
- loader: 'imports-loader',
+ loader: 'imports',
query: {
$: 'jquery',
jQuery: 'jquery'
@@ -46,7 +47,7 @@ module.exports = {
},
{
test: /\.(png|eot|tiff|svg|woff2|woff|ttf|gif)$/,
- loader: 'file-loader',
+ loader: 'file',
query: {
name: 'files/[hash].[ext]'
}
@@ -67,7 +68,22 @@ module.exports = {
htmlExtract,
new CopyWebpackPlugin([
{from: 'images/emoji', to: 'emoji'}
- ])
+ ]),
+ new webpack.optimize.UglifyJsPlugin({
+ 'screw-ie8': true,
+ mangle: {
+ toplevel: false
+ },
+ compress: {
+ warnings: false
+ },
+ comments: false
+ }),
+ new webpack.optimize.AggressiveMergingPlugin(),
+ new webpack.LoaderOptionsPlugin({
+ minimize: true,
+ debug: false
+ })
],
resolve: {
alias: {