This commit is contained in:
=Corey Hulen
2015-09-18 09:25:47 -07:00
22 changed files with 515 additions and 27 deletions

3
.gitignore vendored
View File

@@ -6,7 +6,8 @@ dist
npm-debug.log npm-debug.log
bundle*.js bundle*.js
model/version.go
model/version.go.bak
# Compiled Object files, Static and Dynamic libs (Shared Objects) # Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o *.o

View File

@@ -1,6 +1,10 @@
language: go language: go
go: go:
- 1.4.2 - 1.4.2
- 1.5.1
env:
- TRAVIS_DB=mysql
- TRAVIS_DB=postgres
before_install: before_install:
- gem install compass - gem install compass
- sudo apt-get update -qq - sudo apt-get update -qq
@@ -24,6 +28,9 @@ before_script:
- mysql -e "CREATE DATABASE IF NOT EXISTS mattermost_test ;" -uroot - mysql -e "CREATE DATABASE IF NOT EXISTS mattermost_test ;" -uroot
- mysql -e "CREATE USER 'mmuser'@'%' IDENTIFIED BY 'mostest' ;" -uroot - mysql -e "CREATE USER 'mmuser'@'%' IDENTIFIED BY 'mostest' ;" -uroot
- mysql -e "GRANT ALL ON mattermost_test.* TO 'mmuser'@'%' ;" -uroot - mysql -e "GRANT ALL ON mattermost_test.* TO 'mmuser'@'%' ;" -uroot
- psql -c "create database mattermost_test ;" -U postgres
- psql -c "create user mmuser with password 'mostest' ;" -U postgres
- psql -c 'grant all privileges on database "mattermost_test" to mmuser ;' -U postgres
services: services:
- redis-server - redis-server
addons: addons:
@@ -38,6 +45,8 @@ deploy:
on: on:
repo: mattermost/platform repo: mattermost/platform
tags: true tags: true
go: 1.4.2
condition: $TRAVIS_DB = mysql
- provider: s3 - provider: s3
access_key_id: AKIAJCO3KJYEGWJIKDIQ access_key_id: AKIAJCO3KJYEGWJIKDIQ
@@ -52,3 +61,5 @@ deploy:
on: on:
repo: mattermost/platform repo: mattermost/platform
branch: master branch: master
go: 1.4.2
condition: $TRAVIS_DB = mysql

View File

@@ -3,6 +3,8 @@
GOPATH ?= $(GOPATH:) GOPATH ?= $(GOPATH:)
GOFLAGS ?= $(GOFLAGS:) GOFLAGS ?= $(GOFLAGS:)
BUILD_NUMBER ?= $(BUILD_NUMBER:) BUILD_NUMBER ?= $(BUILD_NUMBER:)
BUILD_DATE = $(shell date -u)
BUILD_HASH = $(shell git rev-parse HEAD)
GO=$(GOPATH)/bin/godep go GO=$(GOPATH)/bin/godep go
ESLINT=node_modules/eslint/bin/eslint.js ESLINT=node_modules/eslint/bin/eslint.js
@@ -32,6 +34,11 @@ all: travis
travis: travis:
@echo building for travis @echo building for travis
if [ "$(TRAVIS_DB)" = "postgres" ]; then \
sed -i'.bak' 's|mysql|postgres|g' config/config.json; \
sed -i'.bak' 's|mmuser:mostest@tcp(dockerhost:3306)/mattermost_test?charset=utf8mb4,utf8|postgres://mmuser:mostest@dockerhost:5432/mattermost_test?sslmode=disable\&connect_timeout=10|g' config/config.json; \
fi
rm -Rf $(DIST_ROOT) rm -Rf $(DIST_ROOT)
@$(GO) clean $(GOFLAGS) -i ./... @$(GO) clean $(GOFLAGS) -i ./...
@@ -49,6 +56,10 @@ travis:
exit 1; \ exit 1; \
fi fi
@sed -i'.bak' 's|_BUILD_NUMBER_|$(BUILD_NUMBER)|g' ./model/version.go
@sed -i'.bak' 's|_BUILD_DATE_|$(BUILD_DATE)|g' ./model/version.go
@sed -i'.bak' 's|_BUILD_HASH_|$(BUILD_HASH)|g' ./model/version.go
@$(GO) build $(GOFLAGS) ./... @$(GO) build $(GOFLAGS) ./...
@$(GO) install $(GOFLAGS) ./... @$(GO) install $(GOFLAGS) ./...
@@ -222,6 +233,10 @@ cleandb:
fi fi
dist: install dist: install
@sed -i'.bak' 's|_BUILD_NUMBER_|$(BUILD_NUMBER)|g' ./model/version.go
@sed -i'.bak' 's|_BUILD_DATE_|$(BUILD_DATE)|g' ./model/version.go
@sed -i'.bak' 's|_BUILD_HASH_|$(BUILD_HASH)|g' ./model/version.go
@$(GO) build $(GOFLAGS) -i ./... @$(GO) build $(GOFLAGS) -i ./...
@$(GO) install $(GOFLAGS) ./... @$(GO) install $(GOFLAGS) ./...

View File

@@ -125,7 +125,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
c.setSiteURL(protocol + "://" + r.Host) c.setSiteURL(protocol + "://" + r.Host)
w.Header().Set(model.HEADER_REQUEST_ID, c.RequestId) w.Header().Set(model.HEADER_REQUEST_ID, c.RequestId)
w.Header().Set(model.HEADER_VERSION_ID, utils.Cfg.ServiceSettings.Version+fmt.Sprintf(".%v", utils.CfgLastModified)) w.Header().Set(model.HEADER_VERSION_ID, fmt.Sprintf("%v.%v", model.CurrentVersion, utils.CfgLastModified))
// Instruct the browser not to display us in an iframe for anti-clickjacking // Instruct the browser not to display us in an iframe for anti-clickjacking
if !h.isApi { if !h.isApi {

View File

@@ -23,6 +23,7 @@ import (
var flagCmdCreateTeam bool var flagCmdCreateTeam bool
var flagCmdCreateUser bool var flagCmdCreateUser bool
var flagCmdAssignRole bool var flagCmdAssignRole bool
var flagCmdVersion bool
var flagCmdResetPassword bool var flagCmdResetPassword bool
var flagConfigFile string var flagConfigFile string
var flagEmail string var flagEmail string
@@ -42,6 +43,7 @@ func main() {
} }
pwd, _ := os.Getwd() pwd, _ := os.Getwd()
l4g.Info("Current version is %v (%v/%v/%v)", model.CurrentVersion, model.BuildNumber, model.BuildDate, model.BuildHash)
l4g.Info("Current working directory is %v", pwd) l4g.Info("Current working directory is %v", pwd)
l4g.Info("Loaded config file from %v", utils.FindConfigFile(flagConfigFile)) l4g.Info("Loaded config file from %v", utils.FindConfigFile(flagConfigFile))
@@ -83,14 +85,16 @@ func parseCmds() {
flag.BoolVar(&flagCmdCreateTeam, "create_team", false, "") flag.BoolVar(&flagCmdCreateTeam, "create_team", false, "")
flag.BoolVar(&flagCmdCreateUser, "create_user", false, "") flag.BoolVar(&flagCmdCreateUser, "create_user", false, "")
flag.BoolVar(&flagCmdAssignRole, "assign_role", false, "") flag.BoolVar(&flagCmdAssignRole, "assign_role", false, "")
flag.BoolVar(&flagCmdVersion, "version", false, "")
flag.BoolVar(&flagCmdResetPassword, "reset_password", false, "") flag.BoolVar(&flagCmdResetPassword, "reset_password", false, "")
flag.Parse() flag.Parse()
flagRunCmds = flagCmdCreateTeam || flagCmdCreateUser || flagCmdAssignRole || flagCmdResetPassword flagRunCmds = flagCmdCreateTeam || flagCmdCreateUser || flagCmdAssignRole || flagCmdResetPassword || flagCmdVersion
} }
func runCmds() { func runCmds() {
cmdVersion()
cmdCreateTeam() cmdCreateTeam()
cmdCreateUser() cmdCreateUser()
cmdAssignRole() cmdAssignRole()
@@ -184,6 +188,17 @@ func cmdCreateUser() {
} }
} }
func cmdVersion() {
if flagCmdVersion {
fmt.Fprintln(os.Stderr, "Version: "+model.CurrentVersion)
fmt.Fprintln(os.Stderr, "Build Number: "+model.BuildNumber)
fmt.Fprintln(os.Stderr, "Build Date: "+model.BuildDate)
fmt.Fprintln(os.Stderr, "Build Hash: "+model.BuildHash)
os.Exit(0)
}
}
func cmdAssignRole() { func cmdAssignRole() {
if flagCmdAssignRole { if flagCmdAssignRole {
if len(flagTeamName) == 0 { if len(flagTeamName) == 0 {
@@ -298,6 +313,8 @@ Usage:
platform [options] platform [options]
-version Display the current version
-config="config.json" Path to the config file -config="config.json" Path to the config file
-email="user@example.com" Email address used in other commands -email="user@example.com" Email address used in other commands

34
model/system.go Normal file
View File

@@ -0,0 +1,34 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
type System struct {
Name string `json:"name"`
Value string `json:"value"`
}
func (o *System) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
} else {
return string(b)
}
}
func SystemFromJson(data io.Reader) *System {
decoder := json.NewDecoder(data)
var o System
err := decoder.Decode(&o)
if err == nil {
return &o
} else {
return nil
}
}

19
model/system_test.go Normal file
View File

@@ -0,0 +1,19 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"strings"
"testing"
)
func TestSystemJson(t *testing.T) {
system := System{Name: "test", Value: NewId()}
json := system.ToJson()
result := SystemFromJson(strings.NewReader(json))
if result.Name != "test" {
t.Fatal("Ids do not match")
}
}

View File

@@ -16,11 +16,6 @@ import (
"time" "time"
) )
const (
// Also change web/react/stores/browser_store.jsx BROWSER_STORE_VERSION
ETAG_ROOT_VERSION = "12"
)
type StringMap map[string]string type StringMap map[string]string
type StringArray []string type StringArray []string
type EncryptStringMap map[string]string type EncryptStringMap map[string]string
@@ -235,7 +230,7 @@ func IsValidAlphaNum(s string, allowUnderscores bool) bool {
func Etag(parts ...interface{}) string { func Etag(parts ...interface{}) string {
etag := ETAG_ROOT_VERSION etag := CurrentVersion
for _, part := range parts { for _, part := range parts {
etag += fmt.Sprintf(".%v", part) etag += fmt.Sprintf(".%v", part)

90
model/version.go Normal file
View File

@@ -0,0 +1,90 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"strconv"
"strings"
)
// This is a list of all the current viersions including any patches.
// It should be maitained in chronological order with most current
// release at the front of the list.
var versions = []string{
"0.8.0",
"0.7.1",
"0.7.0",
"0.6.0",
"0.5.0",
}
var CurrentVersion string = versions[0]
var BuildNumber = "_BUILD_NUMBER_"
var BuildDate = "_BUILD_DATE_"
var BuildHash = "_BUILD_HASH_"
func SplitVersion(version string) (int64, int64, int64) {
parts := strings.Split(version, ".")
major := int64(0)
minor := int64(0)
patch := int64(0)
if len(parts) > 0 {
major, _ = strconv.ParseInt(parts[0], 10, 64)
}
if len(parts) > 1 {
minor, _ = strconv.ParseInt(parts[1], 10, 64)
}
if len(parts) > 2 {
patch, _ = strconv.ParseInt(parts[2], 10, 64)
}
return major, minor, patch
}
func GetPreviousVersion(currentVersion string) (int64, int64) {
currentIndex := -1
currentMajor, currentMinor, _ := SplitVersion(currentVersion)
for index, version := range versions {
major, minor, _ := SplitVersion(version)
if currentMajor == major && currentMinor == minor {
currentIndex = index
}
if currentIndex >= 0 {
if currentMajor != major || currentMinor != minor {
return major, minor
}
}
}
return 0, 0
}
func IsCurrentVersion(versionToCheck string) bool {
currentMajor, currentMinor, _ := SplitVersion(CurrentVersion)
toCheckMajor, toCheckMinor, _ := SplitVersion(versionToCheck)
if toCheckMajor == currentMajor && toCheckMinor == currentMinor {
return true
} else {
return false
}
}
func IsPreviousVersion(versionToCheck string) bool {
toCheckMajor, toCheckMinor, _ := SplitVersion(versionToCheck)
prevMajor, prevMinor := GetPreviousVersion(CurrentVersion)
if toCheckMajor == prevMajor && toCheckMinor == prevMinor {
return true
} else {
return false
}
}

74
model/version_test.go Normal file
View File

@@ -0,0 +1,74 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"fmt"
"testing"
)
func TestSplitVersion(t *testing.T) {
major1, minor1, patch1 := SplitVersion("junk")
if major1 != 0 || minor1 != 0 || patch1 != 0 {
t.Fatal()
}
major2, minor2, patch2 := SplitVersion("1.2.3")
if major2 != 1 || minor2 != 2 || patch2 != 3 {
t.Fatal()
}
major3, minor3, patch3 := SplitVersion("1.2")
if major3 != 1 || minor3 != 2 || patch3 != 0 {
t.Fatal()
}
major4, minor4, patch4 := SplitVersion("1")
if major4 != 1 || minor4 != 0 || patch4 != 0 {
t.Fatal()
}
major5, minor5, patch5 := SplitVersion("1.2.3.junkgoeswhere")
if major5 != 1 || minor5 != 2 || patch5 != 3 {
t.Fatal()
}
}
func TestGetPreviousVersion(t *testing.T) {
if major, minor := GetPreviousVersion("0.8.0"); major != 0 || minor != 7 {
t.Fatal(major, minor)
}
if major, minor := GetPreviousVersion("0.7.0"); major != 0 || minor != 6 {
t.Fatal(major, minor)
}
if major, minor := GetPreviousVersion("0.7.1"); major != 0 || minor != 6 {
t.Fatal(major, minor)
}
if major, minor := GetPreviousVersion("0.7111.1"); major != 0 || minor != 0 {
t.Fatal(major, minor)
}
}
func TestIsCurrentVersion(t *testing.T) {
major, minor, patch := SplitVersion(CurrentVersion)
if !IsCurrentVersion(CurrentVersion) {
t.Fatal()
}
if !IsCurrentVersion(fmt.Sprintf("%v.%v.%v", major, minor, patch+100)) {
t.Fatal()
}
if IsCurrentVersion(fmt.Sprintf("%v.%v.%v", major, minor+1, patch)) {
t.Fatal()
}
if IsCurrentVersion(fmt.Sprintf("%v.%v.%v", major+1, minor, patch)) {
t.Fatal()
}
}

View File

@@ -37,7 +37,6 @@ func NewSqlChannelStore(sqlStore *SqlStore) ChannelStore {
} }
func (s SqlChannelStore) UpgradeSchemaIfNeeded() { func (s SqlChannelStore) UpgradeSchemaIfNeeded() {
s.CreateColumnIfNotExists("Channels", "CreatorId", "varchar(26)", "character varying(26)", "")
} }
func (s SqlChannelStore) CreateIndexesIfNotExists() { func (s SqlChannelStore) CreateIndexesIfNotExists() {

View File

@@ -196,9 +196,9 @@ func (s SqlPostStore) GetEtag(channelId string) StoreChannel {
var et etagPosts var et etagPosts
err := s.GetReplica().SelectOne(&et, "SELECT Id, UpdateAt FROM Posts WHERE ChannelId = :ChannelId ORDER BY UpdateAt DESC LIMIT 1", map[string]interface{}{"ChannelId": channelId}) err := s.GetReplica().SelectOne(&et, "SELECT Id, UpdateAt FROM Posts WHERE ChannelId = :ChannelId ORDER BY UpdateAt DESC LIMIT 1", map[string]interface{}{"ChannelId": channelId})
if err != nil { if err != nil {
result.Data = fmt.Sprintf("%v.0.%v", model.ETAG_ROOT_VERSION, model.GetMillis()) result.Data = fmt.Sprintf("%v.0.%v", model.CurrentVersion, model.GetMillis())
} else { } else {
result.Data = fmt.Sprintf("%v.%v.%v", model.ETAG_ROOT_VERSION, et.Id, et.UpdateAt) result.Data = fmt.Sprintf("%v.%v.%v", model.CurrentVersion, et.Id, et.UpdateAt)
} }
storeChannel <- result storeChannel <- result

View File

@@ -37,14 +37,14 @@ func TestPostStoreGet(t *testing.T) {
o1.Message = "a" + model.NewId() + "b" o1.Message = "a" + model.NewId() + "b"
etag1 := (<-store.Post().GetEtag(o1.ChannelId)).Data.(string) etag1 := (<-store.Post().GetEtag(o1.ChannelId)).Data.(string)
if strings.Index(etag1, model.ETAG_ROOT_VERSION+".0.") != 0 { if strings.Index(etag1, model.CurrentVersion+".0.") != 0 {
t.Fatal("Invalid Etag") t.Fatal("Invalid Etag")
} }
o1 = (<-store.Post().Save(o1)).Data.(*model.Post) o1 = (<-store.Post().Save(o1)).Data.(*model.Post)
etag2 := (<-store.Post().GetEtag(o1.ChannelId)).Data.(string) etag2 := (<-store.Post().GetEtag(o1.ChannelId)).Data.(string)
if strings.Index(etag2, model.ETAG_ROOT_VERSION+"."+o1.Id) != 0 { if strings.Index(etag2, model.CurrentVersion+"."+o1.Id) != 0 {
t.Fatal("Invalid Etag") t.Fatal("Invalid Etag")
} }
@@ -136,7 +136,7 @@ func TestPostStoreDelete(t *testing.T) {
o1.Message = "a" + model.NewId() + "b" o1.Message = "a" + model.NewId() + "b"
etag1 := (<-store.Post().GetEtag(o1.ChannelId)).Data.(string) etag1 := (<-store.Post().GetEtag(o1.ChannelId)).Data.(string)
if strings.Index(etag1, model.ETAG_ROOT_VERSION+".0.") != 0 { if strings.Index(etag1, model.CurrentVersion+".0.") != 0 {
t.Fatal("Invalid Etag") t.Fatal("Invalid Etag")
} }
@@ -160,7 +160,7 @@ func TestPostStoreDelete(t *testing.T) {
} }
etag2 := (<-store.Post().GetEtag(o1.ChannelId)).Data.(string) etag2 := (<-store.Post().GetEtag(o1.ChannelId)).Data.(string)
if strings.Index(etag2, model.ETAG_ROOT_VERSION+"."+o1.Id) != 0 { if strings.Index(etag2, model.CurrentVersion+"."+o1.Id) != 0 {
t.Fatal("Invalid Etag") t.Fatal("Invalid Etag")
} }
} }

View File

@@ -39,6 +39,7 @@ type SqlStore struct {
audit AuditStore audit AuditStore
session SessionStore session SessionStore
oauth OAuthStore oauth OAuthStore
system SystemStore
} }
func NewSqlStore() Store { func NewSqlStore() Store {
@@ -56,9 +57,30 @@ func NewSqlStore() Store {
utils.Cfg.SqlSettings.Trace) utils.Cfg.SqlSettings.Trace)
} }
schemaVersion := sqlStore.GetCurrentSchemaVersion()
// If the version is already set then we are potentially in an 'upgrade needed' state
if schemaVersion != "" {
// Check to see if it's the most current database schema version
if !model.IsCurrentVersion(schemaVersion) {
// If we are upgrading from the previous version then print a warning and continue
if model.IsPreviousVersion(schemaVersion) {
l4g.Warn("The database schema version of " + schemaVersion + " appears to be out of date")
l4g.Warn("Attempting to upgrade the database schema version to " + model.CurrentVersion)
} else {
// If this is an 'upgrade needed' state but the user is attempting to skip a version then halt the world
l4g.Critical("The database schema version of " + schemaVersion + " cannot be upgraded. You must not skip a version.")
time.Sleep(time.Second)
panic("The database schema version of " + schemaVersion + " cannot be upgraded. You must not skip a version.")
}
}
}
// Temporary upgrade code, remove after 0.8.0 release // Temporary upgrade code, remove after 0.8.0 release
if sqlStore.DoesColumnExist("Sessions", "AltId") { if sqlStore.DoesTableExist("Sessions") {
sqlStore.GetMaster().Exec("DROP TABLE IF EXISTS Sessions") if sqlStore.DoesColumnExist("Sessions", "AltId") {
sqlStore.GetMaster().Exec("DROP TABLE IF EXISTS Sessions")
}
} }
sqlStore.team = NewSqlTeamStore(sqlStore) sqlStore.team = NewSqlTeamStore(sqlStore)
@@ -68,6 +90,7 @@ func NewSqlStore() Store {
sqlStore.audit = NewSqlAuditStore(sqlStore) sqlStore.audit = NewSqlAuditStore(sqlStore)
sqlStore.session = NewSqlSessionStore(sqlStore) sqlStore.session = NewSqlSessionStore(sqlStore)
sqlStore.oauth = NewSqlOAuthStore(sqlStore) sqlStore.oauth = NewSqlOAuthStore(sqlStore)
sqlStore.system = NewSqlSystemStore(sqlStore)
sqlStore.master.CreateTablesIfNotExists() sqlStore.master.CreateTablesIfNotExists()
@@ -78,6 +101,7 @@ func NewSqlStore() Store {
sqlStore.audit.(*SqlAuditStore).UpgradeSchemaIfNeeded() sqlStore.audit.(*SqlAuditStore).UpgradeSchemaIfNeeded()
sqlStore.session.(*SqlSessionStore).UpgradeSchemaIfNeeded() sqlStore.session.(*SqlSessionStore).UpgradeSchemaIfNeeded()
sqlStore.oauth.(*SqlOAuthStore).UpgradeSchemaIfNeeded() sqlStore.oauth.(*SqlOAuthStore).UpgradeSchemaIfNeeded()
sqlStore.system.(*SqlSystemStore).UpgradeSchemaIfNeeded()
sqlStore.team.(*SqlTeamStore).CreateIndexesIfNotExists() sqlStore.team.(*SqlTeamStore).CreateIndexesIfNotExists()
sqlStore.channel.(*SqlChannelStore).CreateIndexesIfNotExists() sqlStore.channel.(*SqlChannelStore).CreateIndexesIfNotExists()
@@ -86,6 +110,17 @@ func NewSqlStore() Store {
sqlStore.audit.(*SqlAuditStore).CreateIndexesIfNotExists() sqlStore.audit.(*SqlAuditStore).CreateIndexesIfNotExists()
sqlStore.session.(*SqlSessionStore).CreateIndexesIfNotExists() sqlStore.session.(*SqlSessionStore).CreateIndexesIfNotExists()
sqlStore.oauth.(*SqlOAuthStore).CreateIndexesIfNotExists() sqlStore.oauth.(*SqlOAuthStore).CreateIndexesIfNotExists()
sqlStore.system.(*SqlSystemStore).CreateIndexesIfNotExists()
if model.IsPreviousVersion(schemaVersion) {
sqlStore.system.Update(&model.System{Name: "Version", Value: model.CurrentVersion})
l4g.Warn("The database schema has been upgraded to version " + model.CurrentVersion)
}
if schemaVersion == "" {
sqlStore.system.Save(&model.System{Name: "Version", Value: model.CurrentVersion})
l4g.Info("The database schema has been set to version " + model.CurrentVersion)
}
return sqlStore return sqlStore
} }
@@ -131,6 +166,56 @@ func setupConnection(con_type string, driver string, dataSource string, maxIdle
return dbmap return dbmap
} }
func (ss SqlStore) GetCurrentSchemaVersion() string {
version, _ := ss.GetMaster().SelectStr("SELECT Value FROM Systems WHERE Name='Version'")
return version
}
func (ss SqlStore) DoesTableExist(tableName string) bool {
if utils.Cfg.SqlSettings.DriverName == "postgres" {
count, err := ss.GetMaster().SelectInt(
`SELECT count(relname) FROM pg_class WHERE relname=$1`,
strings.ToLower(tableName),
)
if err != nil {
l4g.Critical("Failed to check if table exists %v", err)
time.Sleep(time.Second)
panic("Failed to check if table exists " + err.Error())
}
return count > 0
} else if utils.Cfg.SqlSettings.DriverName == "mysql" {
count, err := ss.GetMaster().SelectInt(
`SELECT
COUNT(0) AS table_exists
FROM
information_schema.TABLES
WHERE
TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = ?
`,
tableName,
)
if err != nil {
l4g.Critical("Failed to check if table exists %v", err)
time.Sleep(time.Second)
panic("Failed to check if table exists " + err.Error())
}
return count > 0
} else {
l4g.Critical("Failed to check if column exists because of missing driver")
time.Sleep(time.Second)
panic("Failed to check if column exists because of missing driver")
}
}
func (ss SqlStore) DoesColumnExist(tableName string, columnName string) bool { func (ss SqlStore) DoesColumnExist(tableName string, columnName string) bool {
if utils.Cfg.SqlSettings.DriverName == "postgres" { if utils.Cfg.SqlSettings.DriverName == "postgres" {
count, err := ss.GetMaster().SelectInt( count, err := ss.GetMaster().SelectInt(
@@ -380,6 +465,10 @@ func (ss SqlStore) OAuth() OAuthStore {
return ss.oauth return ss.oauth
} }
func (ss SqlStore) System() SystemStore {
return ss.system
}
type mattermConverter struct{} type mattermConverter struct{}
func (me mattermConverter) ToDb(val interface{}) (interface{}, error) { func (me mattermConverter) ToDb(val interface{}) (interface{}, error) {

92
store/sql_system_store.go Normal file
View File

@@ -0,0 +1,92 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
package store
import (
"github.com/mattermost/platform/model"
)
type SqlSystemStore struct {
*SqlStore
}
func NewSqlSystemStore(sqlStore *SqlStore) SystemStore {
s := &SqlSystemStore{sqlStore}
for _, db := range sqlStore.GetAllConns() {
table := db.AddTableWithName(model.System{}, "Systems").SetKeys(false, "Name")
table.ColMap("Name").SetMaxSize(64)
table.ColMap("Value").SetMaxSize(1024)
}
return s
}
func (s SqlSystemStore) UpgradeSchemaIfNeeded() {
}
func (s SqlSystemStore) CreateIndexesIfNotExists() {
}
func (s SqlSystemStore) Save(system *model.System) StoreChannel {
storeChannel := make(StoreChannel)
go func() {
result := StoreResult{}
if err := s.GetMaster().Insert(system); err != nil {
result.Err = model.NewAppError("SqlSystemStore.Save", "We encounted an error saving the system property", "")
}
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (s SqlSystemStore) Update(system *model.System) StoreChannel {
storeChannel := make(StoreChannel)
go func() {
result := StoreResult{}
if _, err := s.GetMaster().Update(system); err != nil {
result.Err = model.NewAppError("SqlSystemStore.Save", "We encounted an error updating the system property", "")
}
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (s SqlSystemStore) Get() StoreChannel {
storeChannel := make(StoreChannel)
go func() {
result := StoreResult{}
var systems []model.System
props := make(model.StringMap)
if _, err := s.GetReplica().Select(&systems, "SELECT * FROM Systems"); err != nil {
result.Err = model.NewAppError("SqlSystemStore.Get", "We encounted an error finding the system properties", "")
} else {
for _, prop := range systems {
props[prop.Name] = prop.Value
}
result.Data = props
}
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}

View File

@@ -0,0 +1,33 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
package store
import (
"github.com/mattermost/platform/model"
"testing"
)
func TestSqlSystemStore(t *testing.T) {
Setup()
system := &model.System{Name: model.NewId(), Value: "value"}
Must(store.System().Save(system))
result := <-store.System().Get()
systems := result.Data.(model.StringMap)
if systems[system.Name] != system.Value {
t.Fatal()
}
system.Value = "value2"
Must(store.System().Update(system))
result2 := <-store.System().Get()
systems2 := result2.Data.(model.StringMap)
if systems2[system.Name] != system.Value {
t.Fatal()
}
}

View File

@@ -325,9 +325,9 @@ func (s SqlUserStore) GetEtagForProfiles(teamId string) StoreChannel {
updateAt, err := s.GetReplica().SelectInt("SELECT UpdateAt FROM Users WHERE TeamId = :TeamId ORDER BY UpdateAt DESC LIMIT 1", map[string]interface{}{"TeamId": teamId}) updateAt, err := s.GetReplica().SelectInt("SELECT UpdateAt FROM Users WHERE TeamId = :TeamId ORDER BY UpdateAt DESC LIMIT 1", map[string]interface{}{"TeamId": teamId})
if err != nil { if err != nil {
result.Data = fmt.Sprintf("%v.%v", model.ETAG_ROOT_VERSION, model.GetMillis()) result.Data = fmt.Sprintf("%v.%v", model.CurrentVersion, model.GetMillis())
} else { } else {
result.Data = fmt.Sprintf("%v.%v", model.ETAG_ROOT_VERSION, updateAt) result.Data = fmt.Sprintf("%v.%v", model.CurrentVersion, updateAt)
} }
storeChannel <- result storeChannel <- result

View File

@@ -35,6 +35,7 @@ type Store interface {
Audit() AuditStore Audit() AuditStore
Session() SessionStore Session() SessionStore
OAuth() OAuthStore OAuth() OAuthStore
System() SystemStore
Close() Close()
} }
@@ -130,3 +131,9 @@ type OAuthStore interface {
GetAccessDataByAuthCode(authCode string) StoreChannel GetAccessDataByAuthCode(authCode string) StoreChannel
RemoveAccessData(token string) StoreChannel RemoveAccessData(token string) StoreChannel
} }
type SystemStore interface {
Save(system *model.System) StoreChannel
Update(system *model.System) StoreChannel
Get() StoreChannel
}

View File

@@ -170,7 +170,11 @@ func getSanitizeOptions(c *model.Config) map[string]bool {
func getClientProperties(c *model.Config) map[string]string { func getClientProperties(c *model.Config) map[string]string {
props := make(map[string]string) props := make(map[string]string)
props["Version"] = c.ServiceSettings.Version props["Version"] = model.CurrentVersion
props["BuildNumber"] = model.BuildNumber
props["BuildDate"] = model.BuildDate
props["BuildHash"] = model.BuildHash
props["SiteName"] = c.ServiceSettings.SiteName props["SiteName"] = c.ServiceSettings.SiteName
props["ByPassEmail"] = strconv.FormatBool(c.EmailSettings.ByPassEmail) props["ByPassEmail"] = strconv.FormatBool(c.EmailSettings.ByPassEmail)
props["FeedbackEmail"] = c.EmailSettings.FeedbackEmail props["FeedbackEmail"] = c.EmailSettings.FeedbackEmail

View File

@@ -251,6 +251,17 @@ export default class SecurityTab extends React.Component {
<div className='divider-dark first'/> <div className='divider-dark first'/>
{passwordSection} {passwordSection}
<div className='divider-dark'/> <div className='divider-dark'/>
<ul
className='section-min'
>
<li className='col-sm-10 section-title'>{'Version ' + global.window.config.Version}</li>
<li className='col-sm-7 section-describe'>
<div className='text-nowrap'>{'Build Number: ' + global.window.config.BuildNumber}</div>
<div className='text-nowrap'>{'Build Date: ' + global.window.config.BuildDate}</div>
<div className='text-nowrap'>{'Build Hash: ' + global.window.config.BuildHash}</div>
</li>
</ul>
<div className='divider-dark'/>
<br></br> <br></br>
<a <a
data-toggle='modal' data-toggle='modal'

View File

@@ -9,9 +9,6 @@ function getPrefix() {
return UserStore.getCurrentId() + '_'; return UserStore.getCurrentId() + '_';
} }
// Also change model/utils.go ETAG_ROOT_VERSION
var BROWSER_STORE_VERSION = '.5';
class BrowserStoreClass { class BrowserStoreClass {
constructor() { constructor() {
this.getItem = this.getItem.bind(this); this.getItem = this.getItem.bind(this);
@@ -25,9 +22,9 @@ class BrowserStoreClass {
this.isLocalStorageSupported = this.isLocalStorageSupported.bind(this); this.isLocalStorageSupported = this.isLocalStorageSupported.bind(this);
var currentVersion = localStorage.getItem('local_storage_version'); var currentVersion = localStorage.getItem('local_storage_version');
if (currentVersion !== BROWSER_STORE_VERSION) { if (currentVersion !== global.window.config.Version) {
this.clear(); this.clear();
localStorage.setItem('local_storage_version', BROWSER_STORE_VERSION); localStorage.setItem('local_storage_version', global.window.config.Version);
} }
} }

View File

@@ -56,7 +56,7 @@ function autolinkUrls(text, tokens) {
const linkText = match.getMatchedText(); const linkText = match.getMatchedText();
let url = linkText; let url = linkText;
if (!url.lastIndexOf('http', 0) === 0) { if (url.lastIndexOf('http', 0) !== 0) {
url = `http://${linkText}`; url = `http://${linkText}`;
} }