Этот коммит содержится в:
=Corey Hulen
2016-01-22 16:31:58 -06:00
родитель 6e2c1b7fd5 d352c5b64d
Коммит c2d98c2c1f
1772 изменённых файлов: 1556763 добавлений и 520 удалений

2
.gitignore поставляемый
Просмотреть файл

@@ -67,3 +67,5 @@ api/data/*
.agignore
.ctags
tags
model/version.go.bak

Просмотреть файл

@@ -15,6 +15,8 @@ script: make dist-travis
addons:
hosts:
- 127.0.0.1 dockerhost
after_success:
- cd dist && curl -F "pr_number=$TRAVIS_PULL_REQUEST" -F "file=@mattermost.tar.gz" mattermod.mattermost.com:8087/upload_pr_build && cd ..
before_deploy:
- sudo rm -rf dist/mattermost
- sudo chown `whoami` dist/mattermost.tar.gz

13
Godeps/Godeps.json сгенерированный
Просмотреть файл

@@ -11,6 +11,10 @@
"Comment": "0.4.0-9-g3fdfada",
"Rev": "3fdfadabc96863ceec055bd73ab1e80324e72706"
},
{
"ImportPath": "github.com/cloudfoundry/jibber_jabber",
"Rev": "bcc4c8345a21301bf47c032ff42dd1aae2fe3027"
},
{
"ImportPath": "github.com/disintegration/imaging",
"Rev": "546cb3c5137b3f1232e123a26aa033aade6b3066"
@@ -77,6 +81,11 @@
"Comment": "v0.4.1-5-g783ec61",
"Rev": "783ec61292aee3fc2f442ce740aa491e4849b794"
},
{
"ImportPath": "github.com/nicksnyder/go-i18n/i18n",
"Comment": "v1.4.0",
"Rev": "37e5c2de3e03e4b82693e3fcb4a6aa2cc4eb07e3"
},
{
"ImportPath": "github.com/pborman/uuid",
"Rev": "dee7705ef7b324f27ceb85a121c61f2c2e8ce988"
@@ -131,6 +140,10 @@
"ImportPath": "gopkg.in/throttled/throttled.v1",
"Comment": "v1.0.0",
"Rev": "74e328a1af88a9b54f9eca1397d74ad98572a6df"
},
{
"ImportPath": "gopkg.in/yaml.v2",
"Rev": "f7716cbe52baa25d2e9b0d0da546fcf909fc16b4"
}
]
}

11
Godeps/_workspace/src/github.com/cloudfoundry/jibber_jabber/.travis.yml сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,11 @@
language: go
go:
- 1.2
before_install:
- go get github.com/onsi/ginkgo/...
- go get github.com/onsi/gomega/...
- go install github.com/onsi/ginkgo/ginkgo
script: PATH=$PATH:$HOME/gopath/bin ginkgo -r .
branches:
only:
- master

201
Godeps/_workspace/src/github.com/cloudfoundry/jibber_jabber/LICENSE сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2014 Pivotal
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file 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.

44
Godeps/_workspace/src/github.com/cloudfoundry/jibber_jabber/README.md сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,44 @@
# Jibber Jabber [![Build Status](https://travis-ci.org/cloudfoundry/jibber_jabber.svg?branch=master)](https://travis-ci.org/cloudfoundry/jibber_jabber)
Jibber Jabber is a GoLang Library that can be used to detect an operating system's current language.
### OS Support
OSX and Linux via the `LC_ALL` and `LANG` environment variables. These are standard variables that are used in ALL versions of UNIX for language detection.
Windows via [GetUserDefaultLocaleName](http://msdn.microsoft.com/en-us/library/windows/desktop/dd318136.aspx) and [GetSystemDefaultLocaleName](http://msdn.microsoft.com/en-us/library/windows/desktop/dd318122.aspx) system calls. These calls are supported in Windows Vista and up.
# Usage
Add the following line to your go `import`:
```
"github.com/cloudfoundry/jibber_jabber"
```
### DetectIETF
`DetectIETF` will return the current locale as a string. The format of the locale will be the [ISO 639](http://en.wikipedia.org/wiki/ISO_639) two-letter language code, a DASH, then an [ISO 3166](http://en.wikipedia.org/wiki/ISO_3166-1) two-letter country code.
```
userLocale, err := jibber_jabber.DetectIETF()
println("Locale:", userLocale)
```
### DetectLanguage
`DetectLanguage` will return the current languge as a string. The format will be the [ISO 639](http://en.wikipedia.org/wiki/ISO_639) two-letter language code.
```
userLanguage, err := jibber_jabber.DetectLanguage()
println("Language:", userLanguage)
```
### DetectTerritory
`DetectTerritory` will return the current locale territory as a string. The format will be the [ISO 3166](http://en.wikipedia.org/wiki/ISO_3166-1) two-letter country code.
```
localeTerritory, err := jibber_jabber.DetectTerritory()
println("Territory:", localeTerritory)
```
### Errors
All the Detect commands will return an error if they are unable to read the Locale from the system.
For Windows, additional error information is provided due to the nature of the system call being used.

5
Godeps/_workspace/src/github.com/cloudfoundry/jibber_jabber/ci/scripts/windows-64-test.bat сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,5 @@
git fetch
git checkout %GIT_COMMIT%
SET GOPATH=%CD%\Godeps\_workspace;c:\Users\Administrator\go
c:\Go\bin\go test -v .

22
Godeps/_workspace/src/github.com/cloudfoundry/jibber_jabber/jibber_jabber.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,22 @@
package jibber_jabber
import (
"strings"
)
const (
COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE = "Could not detect Language"
)
func splitLocale(locale string) (string, string) {
formattedLocale := strings.Split(locale, ".")[0]
formattedLocale = strings.Replace(formattedLocale, "-", "_", -1)
pieces := strings.Split(formattedLocale, "_")
language := pieces[0]
territory := ""
if len(pieces) > 1 {
territory = strings.Split(formattedLocale, "_")[1]
}
return language, territory
}

57
Godeps/_workspace/src/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,57 @@
// +build darwin freebsd linux netbsd openbsd
package jibber_jabber
import (
"errors"
"os"
"strings"
)
func getLangFromEnv() (locale string) {
locale = os.Getenv("LC_ALL")
if locale == "" {
locale = os.Getenv("LANG")
}
return
}
func getUnixLocale() (unix_locale string, err error) {
unix_locale = getLangFromEnv()
if unix_locale == "" {
err = errors.New(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE)
}
return
}
func DetectIETF() (locale string, err error) {
unix_locale, err := getUnixLocale()
if err == nil {
language, territory := splitLocale(unix_locale)
locale = language
if territory != "" {
locale = strings.Join([]string{language, territory}, "-")
}
}
return
}
func DetectLanguage() (language string, err error) {
unix_locale, err := getUnixLocale()
if err == nil {
language, _ = splitLocale(unix_locale)
}
return
}
func DetectTerritory() (territory string, err error) {
unix_locale, err := getUnixLocale()
if err == nil {
_, territory = splitLocale(unix_locale)
}
return
}

114
Godeps/_workspace/src/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,114 @@
// +build windows
package jibber_jabber
import (
"errors"
"syscall"
"unsafe"
)
const LOCALE_NAME_MAX_LENGTH uint32 = 85
var SUPPORTED_LOCALES = map[uintptr]string{
0x0407: "de-DE",
0x0409: "en-US",
0x0c0a: "es-ES", //or is it 0x040a
0x040c: "fr-FR",
0x0410: "it-IT",
0x0411: "ja-JA",
0x0412: "ko_KR",
0x0416: "pt-BR",
//0x0419: "ru_RU", - Will add support for Russian when nicksnyder/go-i18n supports Russian
0x0804: "zh-CN",
0x0c04: "zh-HK",
0x0404: "zh-TW",
}
func getWindowsLocaleFrom(sysCall string) (locale string, err error) {
buffer := make([]uint16, LOCALE_NAME_MAX_LENGTH)
dll := syscall.MustLoadDLL("kernel32")
proc := dll.MustFindProc(sysCall)
r, _, dllError := proc.Call(uintptr(unsafe.Pointer(&buffer[0])), uintptr(LOCALE_NAME_MAX_LENGTH))
if r == 0 {
err = errors.New(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE + ":\n" + dllError.Error())
return
}
locale = syscall.UTF16ToString(buffer)
return
}
func getAllWindowsLocaleFrom(sysCall string) (string, error) {
dll, err := syscall.LoadDLL("kernel32")
if err != nil {
return "", errors.New("Could not find kernel32 dll")
}
proc, err := dll.FindProc(sysCall)
if err != nil {
return "", err
}
locale, _, dllError := proc.Call()
if locale == 0 {
return "", errors.New(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE + ":\n" + dllError.Error())
}
return SUPPORTED_LOCALES[locale], nil
}
func getWindowsLocale() (locale string, err error) {
dll, err := syscall.LoadDLL("kernel32")
if err != nil {
return "", errors.New("Could not find kernel32 dll")
}
proc, err := dll.FindProc("GetVersion")
if err != nil {
return "", err
}
v, _, _ := proc.Call()
windowsVersion := byte(v)
isVistaOrGreater := (windowsVersion >= 6)
if isVistaOrGreater {
locale, err = getWindowsLocaleFrom("GetUserDefaultLocaleName")
if err != nil {
locale, err = getWindowsLocaleFrom("GetSystemDefaultLocaleName")
}
} else if !isVistaOrGreater {
locale, err = getAllWindowsLocaleFrom("GetUserDefaultLCID")
if err != nil {
locale, err = getAllWindowsLocaleFrom("GetSystemDefaultLCID")
}
} else {
panic(v)
}
return
}
func DetectIETF() (locale string, err error) {
locale, err = getWindowsLocale()
return
}
func DetectLanguage() (language string, err error) {
windows_locale, err := getWindowsLocale()
if err == nil {
language, _ = splitLocale(windows_locale)
}
return
}
func DetectTerritory() (territory string, err error) {
windows_locale, err := getWindowsLocale()
if err == nil {
_, territory = splitLocale(windows_locale)
}
return
}

19
Godeps/_workspace/src/github.com/nicksnyder/go-i18n/LICENSE сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,19 @@
Copyright (c) 2014 Nick Snyder https://github.com/nicksnyder
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

315
Godeps/_workspace/src/github.com/nicksnyder/go-i18n/i18n/bundle/bundle.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,315 @@
// Package bundle manages translations for multiple languages.
package bundle
import (
"encoding/json"
"fmt"
"gopkg.in/yaml.v2"
"io/ioutil"
"reflect"
"path/filepath"
"github.com/nicksnyder/go-i18n/i18n/language"
"github.com/nicksnyder/go-i18n/i18n/translation"
)
// TranslateFunc is a copy of i18n.TranslateFunc to avoid a circular dependency.
type TranslateFunc func(translationID string, args ...interface{}) string
// Bundle stores the translations for multiple languages.
type Bundle struct {
// The primary translations for a language tag and translation id.
translations map[string]map[string]translation.Translation
// Translations that can be used when an exact language match is not possible.
fallbackTranslations map[string]map[string]translation.Translation
}
// New returns an empty bundle.
func New() *Bundle {
return &Bundle{
translations: make(map[string]map[string]translation.Translation),
fallbackTranslations: make(map[string]map[string]translation.Translation),
}
}
// MustLoadTranslationFile is similar to LoadTranslationFile
// except it panics if an error happens.
func (b *Bundle) MustLoadTranslationFile(filename string) {
if err := b.LoadTranslationFile(filename); err != nil {
panic(err)
}
}
// LoadTranslationFile loads the translations from filename into memory.
//
// The language that the translations are associated with is parsed from the filename (e.g. en-US.json).
//
// Generally you should load translation files once during your program's initialization.
func (b *Bundle) LoadTranslationFile(filename string) error {
buf, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
return b.ParseTranslationFileBytes(filename, buf)
}
// ParseTranslationFileBytes is similar to LoadTranslationFile except it parses the bytes in buf.
//
// It is useful for parsing translation files embedded with go-bindata.
func (b *Bundle) ParseTranslationFileBytes(filename string, buf []byte) error {
basename := filepath.Base(filename)
langs := language.Parse(basename)
switch l := len(langs); {
case l == 0:
return fmt.Errorf("no language found in %q", basename)
case l > 1:
return fmt.Errorf("multiple languages found in filename %q: %v; expected one", basename, langs)
}
translations, err := parseTranslations(filename, buf)
if err != nil {
return err
}
b.AddTranslation(langs[0], translations...)
return nil
}
func parseTranslations(filename string, buf []byte) ([]translation.Translation, error) {
var unmarshalFunc func([]byte, interface{}) error
switch format := filepath.Ext(filename); format {
case ".json":
unmarshalFunc = json.Unmarshal
case ".yaml":
unmarshalFunc = yaml.Unmarshal
default:
return nil, fmt.Errorf("unsupported file extension %s", format)
}
var translationsData []map[string]interface{}
if len(buf) > 0 {
if err := unmarshalFunc(buf, &translationsData); err != nil {
return nil, err
}
}
translations := make([]translation.Translation, 0, len(translationsData))
for i, translationData := range translationsData {
t, err := translation.NewTranslation(translationData)
if err != nil {
return nil, fmt.Errorf("unable to parse translation #%d in %s because %s\n%v", i, filename, err, translationData)
}
translations = append(translations, t)
}
return translations, nil
}
// AddTranslation adds translations for a language.
//
// It is useful if your translations are in a format not supported by LoadTranslationFile.
func (b *Bundle) AddTranslation(lang *language.Language, translations ...translation.Translation) {
if b.translations[lang.Tag] == nil {
b.translations[lang.Tag] = make(map[string]translation.Translation, len(translations))
}
currentTranslations := b.translations[lang.Tag]
for _, newTranslation := range translations {
if currentTranslation := currentTranslations[newTranslation.ID()]; currentTranslation != nil {
currentTranslations[newTranslation.ID()] = currentTranslation.Merge(newTranslation)
} else {
currentTranslations[newTranslation.ID()] = newTranslation
}
}
// lang can provide translations for less specific language tags.
for _, tag := range lang.MatchingTags() {
b.fallbackTranslations[tag] = currentTranslations
}
}
// Translations returns all translations in the bundle.
func (b *Bundle) Translations() map[string]map[string]translation.Translation {
return b.translations
}
// LanguageTags returns the tags of all languages that that have been added.
func (b *Bundle) LanguageTags() []string {
var tags []string
for k := range b.translations {
tags = append(tags, k)
}
return tags
}
// LanguageTranslationIDs returns the ids of all translations that have been added for a given language.
func (b *Bundle) LanguageTranslationIDs(languageTag string) []string {
var ids []string
for id := range b.translations[languageTag] {
ids = append(ids, id)
}
return ids
}
// MustTfunc is similar to Tfunc except it panics if an error happens.
func (b *Bundle) MustTfunc(pref string, prefs ...string) TranslateFunc {
tfunc, err := b.Tfunc(pref, prefs...)
if err != nil {
panic(err)
}
return tfunc
}
// MustTfuncAndLanguage is similar to TfuncAndLanguage except it panics if an error happens.
func (b *Bundle) MustTfuncAndLanguage(pref string, prefs ...string) (TranslateFunc, *language.Language) {
tfunc, language, err := b.TfuncAndLanguage(pref, prefs...)
if err != nil {
panic(err)
}
return tfunc, language
}
// Tfunc is similar to TfuncAndLanguage except is doesn't return the Language.
func (b *Bundle) Tfunc(pref string, prefs ...string) (TranslateFunc, error) {
tfunc, _, err := b.TfuncAndLanguage(pref, prefs...)
return tfunc, err
}
// TfuncAndLanguage returns a TranslateFunc for the first Language that
// has a non-zero number of translations in the bundle.
//
// The returned Language matches the the first language preference that could be satisfied,
// but this may not strictly match the language of the translations used to satisfy that preference.
//
// For example, the user may request "zh". If there are no translations for "zh" but there are translations
// for "zh-cn", then the translations for "zh-cn" will be used but the returned Language will be "zh".
//
// It can parse languages from Accept-Language headers (RFC 2616),
// but it assumes weights are monotonically decreasing.
func (b *Bundle) TfuncAndLanguage(pref string, prefs ...string) (TranslateFunc, *language.Language, error) {
lang := b.supportedLanguage(pref, prefs...)
var err error
if lang == nil {
err = fmt.Errorf("no supported languages found %#v", append(prefs, pref))
}
return func(translationID string, args ...interface{}) string {
return b.translate(lang, translationID, args...)
}, lang, err
}
// supportedLanguage returns the first language which
// has a non-zero number of translations in the bundle.
func (b *Bundle) supportedLanguage(pref string, prefs ...string) *language.Language {
lang := b.translatedLanguage(pref)
if lang == nil {
for _, pref := range prefs {
lang = b.translatedLanguage(pref)
if lang != nil {
break
}
}
}
return lang
}
func (b *Bundle) translatedLanguage(src string) *language.Language {
langs := language.Parse(src)
for _, lang := range langs {
if len(b.translations[lang.Tag]) > 0 ||
len(b.fallbackTranslations[lang.Tag]) > 0 {
return lang
}
}
return nil
}
func (b *Bundle) translate(lang *language.Language, translationID string, args ...interface{}) string {
if lang == nil {
return translationID
}
translations := b.translations[lang.Tag]
if translations == nil {
translations = b.fallbackTranslations[lang.Tag]
if translations == nil {
return translationID
}
}
translation := translations[translationID]
if translation == nil {
return translationID
}
var data interface{}
var count interface{}
if argc := len(args); argc > 0 {
if isNumber(args[0]) {
count = args[0]
if argc > 1 {
data = args[1]
}
} else {
data = args[0]
}
}
if count != nil {
if data == nil {
data = map[string]interface{}{"Count": count}
} else {
dataMap := toMap(data)
dataMap["Count"] = count
data = dataMap
}
}
p, _ := lang.Plural(count)
template := translation.Template(p)
if template == nil {
return translationID
}
s := template.Execute(data)
if s == "" {
return translationID
}
return s
}
func isNumber(n interface{}) bool {
switch n.(type) {
case int, int8, int16, int32, int64, string:
return true
}
return false
}
func toMap(input interface{}) map[string]interface{} {
if data, ok := input.(map[string]interface{}); ok {
return data
}
v := reflect.ValueOf(input)
switch v.Kind() {
case reflect.Ptr:
return toMap(v.Elem().Interface())
case reflect.Struct:
return structToMap(v)
default:
return nil
}
}
// Converts the top level of a struct to a map[string]interface{}.
// Code inspired by github.com/fatih/structs.
func structToMap(v reflect.Value) map[string]interface{} {
out := make(map[string]interface{})
t := v.Type()
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
if field.PkgPath != "" {
// unexported field. skip.
continue
}
out[field.Name] = v.FieldByName(field.Name).Interface()
}
return out
}

152
Godeps/_workspace/src/github.com/nicksnyder/go-i18n/i18n/i18n.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,152 @@
// Package i18n supports string translations with variable substitution and CLDR pluralization.
// It is intended to be used in conjunction with the goi18n command, although that is not strictly required.
//
// Initialization
//
// Your Go program should load translations during its initialization.
// i18n.MustLoadTranslationFile("path/to/fr-FR.all.json")
// If your translations are in a file format not supported by (Must)?LoadTranslationFile,
// then you can use the AddTranslation function to manually add translations.
//
// Fetching a translation
//
// Use Tfunc or MustTfunc to fetch a TranslateFunc that will return the translated string for a specific language.
// func handleRequest(w http.ResponseWriter, r *http.Request) {
// cookieLang := r.Cookie("lang")
// acceptLang := r.Header.Get("Accept-Language")
// defaultLang = "en-US" // known valid language
// T, err := i18n.Tfunc(cookieLang, acceptLang, defaultLang)
// fmt.Println(T("Hello world"))
// }
//
// Usually it is a good idea to identify strings by a generic id rather than the English translation,
// but the rest of this documentation will continue to use the English translation for readability.
// T("Hello world") // ok
// T("programGreeting") // better!
//
// Variables
//
// TranslateFunc supports strings that have variables using the text/template syntax.
// T("Hello {{.Person}}", map[string]interface{}{
// "Person": "Bob",
// })
//
// Pluralization
//
// TranslateFunc supports the pluralization of strings using the CLDR pluralization rules defined here:
// http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
// T("You have {{.Count}} unread emails.", 2)
// T("I am {{.Count}} meters tall.", "1.7")
//
// Plural strings may also have variables.
// T("{{.Person}} has {{.Count}} unread emails", 2, map[string]interface{}{
// "Person": "Bob",
// })
//
// Sentences with multiple plural components can be supported with nesting.
// T("{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.", 3, map[string]interface{}{
// "Person": "Bob",
// "Timeframe": T("{{.Count}} days", 2),
// })
//
// Templates
//
// You can use the .Funcs() method of a text/template or html/template to register a TranslateFunc
// for usage inside of that template.
package i18n
import (
"github.com/nicksnyder/go-i18n/i18n/bundle"
"github.com/nicksnyder/go-i18n/i18n/language"
"github.com/nicksnyder/go-i18n/i18n/translation"
)
// TranslateFunc returns the translation of the string identified by translationID.
//
// If there is no translation for translationID, then the translationID itself is returned.
// This makes it easy to identify missing translations in your app.
//
// If translationID is a non-plural form, then the first variadic argument may be a map[string]interface{}
// or struct that contains template data.
//
// If translationID is a plural form, then the first variadic argument must be an integer type
// (int, int8, int16, int32, int64) or a float formatted as a string (e.g. "123.45").
// The second variadic argument may be a map[string]interface{} or struct that contains template data.
type TranslateFunc func(translationID string, args ...interface{}) string
// IdentityTfunc returns a TranslateFunc that always returns the translationID passed to it.
//
// It is a useful placeholder when parsing a text/template or html/template
// before the actual Tfunc is available.
func IdentityTfunc() TranslateFunc {
return func(translationID string, args ...interface{}) string {
return translationID
}
}
var defaultBundle = bundle.New()
// MustLoadTranslationFile is similar to LoadTranslationFile
// except it panics if an error happens.
func MustLoadTranslationFile(filename string) {
defaultBundle.MustLoadTranslationFile(filename)
}
// LoadTranslationFile loads the translations from filename into memory.
//
// The language that the translations are associated with is parsed from the filename (e.g. en-US.json).
//
// Generally you should load translation files once during your program's initialization.
func LoadTranslationFile(filename string) error {
return defaultBundle.LoadTranslationFile(filename)
}
// ParseTranslationFileBytes is similar to LoadTranslationFile except it parses the bytes in buf.
//
// It is useful for parsing translation files embedded with go-bindata.
func ParseTranslationFileBytes(filename string, buf []byte) error {
return defaultBundle.ParseTranslationFileBytes(filename, buf)
}
// AddTranslation adds translations for a language.
//
// It is useful if your translations are in a format not supported by LoadTranslationFile.
func AddTranslation(lang *language.Language, translations ...translation.Translation) {
defaultBundle.AddTranslation(lang, translations...)
}
// LanguageTags returns the tags of all languages that have been added.
func LanguageTags() []string {
return defaultBundle.LanguageTags()
}
// LanguageTranslationIDs returns the ids of all translations that have been added for a given language.
func LanguageTranslationIDs(languageTag string) []string {
return defaultBundle.LanguageTranslationIDs(languageTag)
}
// MustTfunc is similar to Tfunc except it panics if an error happens.
func MustTfunc(languageSource string, languageSources ...string) TranslateFunc {
return TranslateFunc(defaultBundle.MustTfunc(languageSource, languageSources...))
}
// Tfunc returns a TranslateFunc that will be bound to the first language which
// has a non-zero number of translations.
//
// It can parse languages from Accept-Language headers (RFC 2616).
func Tfunc(languageSource string, languageSources ...string) (TranslateFunc, error) {
tfunc, err := defaultBundle.Tfunc(languageSource, languageSources...)
return TranslateFunc(tfunc), err
}
// MustTfuncAndLanguage is similar to TfuncAndLanguage except it panics if an error happens.
func MustTfuncAndLanguage(languageSource string, languageSources ...string) (TranslateFunc, *language.Language) {
tfunc, lang := defaultBundle.MustTfuncAndLanguage(languageSource, languageSources...)
return TranslateFunc(tfunc), lang
}
// TfuncAndLanguage is similar to Tfunc except it also returns the language which TranslateFunc is bound to.
func TfuncAndLanguage(languageSource string, languageSources ...string) (TranslateFunc, *language.Language, error) {
tfunc, lang, err := defaultBundle.TfuncAndLanguage(languageSource, languageSources...)
return TranslateFunc(tfunc), lang, err
}

5
Godeps/_workspace/src/github.com/nicksnyder/go-i18n/i18n/language/codegen/generate.sh сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,5 @@
#!/bin/sh
go build && ./codegen -cout ../pluralspec_gen.go -tout ../pluralspec_gen_test.go && \
gofmt -w=true ../pluralspec_gen.go && \
gofmt -w=true ../pluralspec_gen_test.go && \
rm codegen

132
Godeps/_workspace/src/github.com/nicksnyder/go-i18n/i18n/language/codegen/main.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,132 @@
package main
import (
"encoding/xml"
"flag"
"fmt"
"io/ioutil"
"os"
"text/template"
)
var usage = `%[1]s generates Go code to support CLDR plural rules.
Usage: %[1]s [options]
Options:
`
func main() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, usage, os.Args[0])
flag.PrintDefaults()
}
var in, cout, tout string
flag.StringVar(&in, "i", "plurals.xml", "the input XML file containing CLDR plural rules")
flag.StringVar(&cout, "cout", "", "the code output file")
flag.StringVar(&tout, "tout", "", "the test output file")
flag.BoolVar(&verbose, "v", false, "verbose output")
flag.Parse()
buf, err := ioutil.ReadFile(in)
if err != nil {
fatalf("failed to read file: %s", err)
}
var data SupplementalData
if err := xml.Unmarshal(buf, &data); err != nil {
fatalf("failed to unmarshal xml: %s", err)
}
count := 0
for _, pg := range data.PluralGroups {
count += len(pg.SplitLocales())
}
infof("parsed %d locales", count)
if cout != "" {
file := openWritableFile(cout)
if err := codeTemplate.Execute(file, data); err != nil {
fatalf("unable to execute code template because %s", err)
} else {
infof("generated %s", cout)
}
} else {
infof("not generating code file (use -cout)")
}
if tout != "" {
file := openWritableFile(tout)
if err := testTemplate.Execute(file, data); err != nil {
fatalf("unable to execute test template because %s", err)
} else {
infof("generated %s", tout)
}
} else {
infof("not generating test file (use -tout)")
}
}
func openWritableFile(name string) *os.File {
file, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
fatalf("failed to write file %s because %s", name, err)
}
return file
}
var codeTemplate = template.Must(template.New("spec").Parse(`package language
// This file is generated by i18n/language/codegen/generate.sh
func init() {
{{range .PluralGroups}}
registerPluralSpec({{printf "%#v" .SplitLocales}}, &PluralSpec{
Plurals: newPluralSet({{range $i, $e := .PluralRules}}{{if $i}}, {{end}}{{$e.CountTitle}}{{end}}),
PluralFunc: func(ops *operands) Plural { {{range .PluralRules}}{{if .GoCondition}}
// {{.Condition}}
if {{.GoCondition}} {
return {{.CountTitle}}
}{{end}}{{end}}
return Other
},
}){{end}}
}
`))
var testTemplate = template.Must(template.New("spec").Parse(`package language
// This file is generated by i18n/language/codegen/generate.sh
import "testing"
{{range .PluralGroups}}
func Test{{.Name}}(t *testing.T) {
var tests []pluralTest
{{range .PluralRules}}
{{if .IntegerExamples}}tests = appendIntegerTests(tests, {{.CountTitle}}, {{printf "%#v" .IntegerExamples}}){{end}}
{{if .DecimalExamples}}tests = appendDecimalTests(tests, {{.CountTitle}}, {{printf "%#v" .DecimalExamples}}){{end}}
{{end}}
locales := {{printf "%#v" .SplitLocales}}
for _, locale := range locales {
runTests(t, locale, tests)
}
}
{{end}}
`))
func infof(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, format+"\n", args...)
}
var verbose bool
func verbosef(format string, args ...interface{}) {
if verbose {
infof(format, args...)
}
}
func fatalf(format string, args ...interface{}) {
infof("fatal: "+format+"\n", args...)
os.Exit(1)
}

230
Godeps/_workspace/src/github.com/nicksnyder/go-i18n/i18n/language/codegen/plurals.xml сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE supplementalData SYSTEM "../../common/dtd/ldmlSupplemental.dtd">
<!--
Copyright © 1991-2015 Unicode, Inc.
CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
For terms of use, see http://www.unicode.org/copyright.html
-->
<supplementalData>
<version number="$Revision: 12002 $"/>
<plurals type="cardinal">
<!-- For a canonicalized list, use GeneratedPluralSamples -->
<!-- 1: other -->
<pluralRules locales="bm bo dz id ig ii in ja jbo jv jw kde kea km ko lkt lo ms my nqo root sah ses sg th to vi wo yo zh">
<pluralRule count="other"> @integer 0~15, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<!-- 2: one,other -->
<pluralRules locales="am as bn fa gu hi kn mr zu">
<pluralRule count="one">i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04</pluralRule>
<pluralRule count="other"> @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~2.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="ff fr hy kab">
<pluralRule count="one">i = 0,1 @integer 0, 1 @decimal 0.0~1.5</pluralRule>
<pluralRule count="other"> @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="ast ca de en et fi fy gl it ji nl sv sw ur yi">
<pluralRule count="one">i = 1 and v = 0 @integer 1</pluralRule>
<pluralRule count="other"> @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="si">
<pluralRule count="one">n = 0,1 or i = 0 and f = 1 @integer 0, 1 @decimal 0.0, 0.1, 1.0, 0.00, 0.01, 1.00, 0.000, 0.001, 1.000, 0.0000, 0.0001, 1.0000</pluralRule>
<pluralRule count="other"> @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.2~0.9, 1.1~1.8, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="ak bh guw ln mg nso pa ti wa">
<pluralRule count="one">n = 0..1 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 1.0000</pluralRule>
<pluralRule count="other"> @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="tzm">
<pluralRule count="one">n = 0..1 or n = 11..99 @integer 0, 1, 11~24 @decimal 0.0, 1.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0</pluralRule>
<pluralRule count="other"> @integer 2~10, 100~106, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="pt">
<pluralRule count="one">n = 0..2 and n != 2 @integer 0, 1 @decimal 0.0, 1.0, 0.00, 1.00, 0.000, 1.000, 0.0000, 1.0000</pluralRule>
<pluralRule count="other"> @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="af asa az bem bez bg brx ce cgg chr ckb dv ee el eo es eu fo fur gsw ha haw hu jgo jmc ka kaj kcg kk kkj kl ks ksb ku ky lb lg mas mgo ml mn nah nb nd ne nn nnh no nr ny nyn om or os pap ps rm rof rwk saq sdh seh sn so sq ss ssy st syr ta te teo tig tk tn tr ts ug uz ve vo vun wae xh xog">
<pluralRule count="one">n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000</pluralRule>
<pluralRule count="other"> @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="pt_PT">
<pluralRule count="one">n = 1 and v = 0 @integer 1</pluralRule>
<pluralRule count="other"> @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="da">
<pluralRule count="one">n = 1 or t != 0 and i = 0,1 @integer 1 @decimal 0.1~1.6</pluralRule>
<pluralRule count="other"> @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 2.0~3.4, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="is">
<pluralRule count="one">t = 0 and i % 10 = 1 and i % 100 != 11 or t != 0 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1~1.6, 10.1, 100.1, 1000.1, …</pluralRule>
<pluralRule count="other"> @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="mk">
<pluralRule count="one">v = 0 and i % 10 = 1 or f % 10 = 1 @integer 1, 11, 21, 31, 41, 51, 61, 71, 101, 1001, … @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …</pluralRule>
<pluralRule count="other"> @integer 0, 2~10, 12~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.2~1.0, 1.2~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="fil tl">
<pluralRule count="one">v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9 @integer 0~3, 5, 7, 8, 10~13, 15, 17, 18, 20, 21, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.3, 0.5, 0.7, 0.8, 1.0~1.3, 1.5, 1.7, 1.8, 2.0, 2.1, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
<pluralRule count="other"> @integer 4, 6, 9, 14, 16, 19, 24, 26, 104, 1004, … @decimal 0.4, 0.6, 0.9, 1.4, 1.6, 1.9, 2.4, 2.6, 10.4, 100.4, 1000.4, …</pluralRule>
</pluralRules>
<!-- 3: zero,one,other -->
<pluralRules locales="lv prg">
<pluralRule count="zero">n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19 @integer 0, 10~20, 30, 40, 50, 60, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
<pluralRule count="one">n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1, 1.0, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …</pluralRule>
<pluralRule count="other"> @integer 2~9, 22~29, 102, 1002, … @decimal 0.2~0.9, 1.2~1.9, 10.2, 100.2, 1000.2, …</pluralRule>
</pluralRules>
<pluralRules locales="lag">
<pluralRule count="zero">n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000</pluralRule>
<pluralRule count="one">i = 0,1 and n != 0 @integer 1 @decimal 0.1~1.6</pluralRule>
<pluralRule count="other"> @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="ksh">
<pluralRule count="zero">n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000</pluralRule>
<pluralRule count="one">n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000</pluralRule>
<pluralRule count="other"> @integer 2~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<!-- 3: one,two,other -->
<pluralRules locales="iu kw naq se sma smi smj smn sms">
<pluralRule count="one">n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000</pluralRule>
<pluralRule count="two">n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000</pluralRule>
<pluralRule count="other"> @integer 0, 3~17, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<!-- 3: one,few,other -->
<pluralRules locales="shi">
<pluralRule count="one">i = 0 or n = 1 @integer 0, 1 @decimal 0.0~1.0, 0.00~0.04</pluralRule>
<pluralRule count="few">n = 2..10 @integer 2~10 @decimal 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 2.00, 3.00, 4.00, 5.00, 6.00, 7.00, 8.00</pluralRule>
<pluralRule count="other"> @integer 11~26, 100, 1000, 10000, 100000, 1000000, … @decimal 1.1~1.9, 2.1~2.7, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="mo ro">
<pluralRule count="one">i = 1 and v = 0 @integer 1</pluralRule>
<pluralRule count="few">v != 0 or n = 0 or n != 1 and n % 100 = 1..19 @integer 0, 2~16, 101, 1001, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
<pluralRule count="other"> @integer 20~35, 100, 1000, 10000, 100000, 1000000, …</pluralRule>
</pluralRules>
<pluralRules locales="bs hr sh sr">
<pluralRule count="one">v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …</pluralRule>
<pluralRule count="few">v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @decimal 0.2~0.4, 1.2~1.4, 2.2~2.4, 3.2~3.4, 4.2~4.4, 5.2, 10.2, 100.2, 1000.2, …</pluralRule>
<pluralRule count="other"> @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.5~1.0, 1.5~2.0, 2.5~2.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<!-- 4: one,two,few,other -->
<pluralRules locales="gd">
<pluralRule count="one">n = 1,11 @integer 1, 11 @decimal 1.0, 11.0, 1.00, 11.00, 1.000, 11.000, 1.0000</pluralRule>
<pluralRule count="two">n = 2,12 @integer 2, 12 @decimal 2.0, 12.0, 2.00, 12.00, 2.000, 12.000, 2.0000</pluralRule>
<pluralRule count="few">n = 3..10,13..19 @integer 3~10, 13~19 @decimal 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 3.00</pluralRule>
<pluralRule count="other"> @integer 0, 20~34, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="sl">
<pluralRule count="one">v = 0 and i % 100 = 1 @integer 1, 101, 201, 301, 401, 501, 601, 701, 1001, …</pluralRule>
<pluralRule count="two">v = 0 and i % 100 = 2 @integer 2, 102, 202, 302, 402, 502, 602, 702, 1002, …</pluralRule>
<pluralRule count="few">v = 0 and i % 100 = 3..4 or v != 0 @integer 3, 4, 103, 104, 203, 204, 303, 304, 403, 404, 503, 504, 603, 604, 703, 704, 1003, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
<pluralRule count="other"> @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …</pluralRule>
</pluralRules>
<pluralRules locales="dsb hsb">
<pluralRule count="one">v = 0 and i % 100 = 1 or f % 100 = 1 @integer 1, 101, 201, 301, 401, 501, 601, 701, 1001, … @decimal 0.1, 1.1, 2.1, 3.1, 4.1, 5.1, 6.1, 7.1, 10.1, 100.1, 1000.1, …</pluralRule>
<pluralRule count="two">v = 0 and i % 100 = 2 or f % 100 = 2 @integer 2, 102, 202, 302, 402, 502, 602, 702, 1002, … @decimal 0.2, 1.2, 2.2, 3.2, 4.2, 5.2, 6.2, 7.2, 10.2, 100.2, 1000.2, …</pluralRule>
<pluralRule count="few">v = 0 and i % 100 = 3..4 or f % 100 = 3..4 @integer 3, 4, 103, 104, 203, 204, 303, 304, 403, 404, 503, 504, 603, 604, 703, 704, 1003, … @decimal 0.3, 0.4, 1.3, 1.4, 2.3, 2.4, 3.3, 3.4, 4.3, 4.4, 5.3, 5.4, 6.3, 6.4, 7.3, 7.4, 10.3, 100.3, 1000.3, …</pluralRule>
<pluralRule count="other"> @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 0.5~1.0, 1.5~2.0, 2.5~2.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<!-- 4: one,two,many,other -->
<pluralRules locales="he iw">
<pluralRule count="one">i = 1 and v = 0 @integer 1</pluralRule>
<pluralRule count="two">i = 2 and v = 0 @integer 2</pluralRule>
<pluralRule count="many">v = 0 and n != 0..10 and n % 10 = 0 @integer 20, 30, 40, 50, 60, 70, 80, 90, 100, 1000, 10000, 100000, 1000000, …</pluralRule>
<pluralRule count="other"> @integer 0, 3~17, 101, 1001, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<!-- 4: one,few,many,other -->
<pluralRules locales="cs sk">
<pluralRule count="one">i = 1 and v = 0 @integer 1</pluralRule>
<pluralRule count="few">i = 2..4 and v = 0 @integer 2~4</pluralRule>
<pluralRule count="many">v != 0 @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
<pluralRule count="other"> @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …</pluralRule>
</pluralRules>
<pluralRules locales="pl">
<pluralRule count="one">i = 1 and v = 0 @integer 1</pluralRule>
<pluralRule count="few">v = 0 and i % 10 = 2..4 and i % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …</pluralRule>
<pluralRule count="many">v = 0 and i != 1 and i % 10 = 0..1 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …</pluralRule>
<pluralRule count="other"> @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="be">
<pluralRule count="one">n % 10 = 1 and n % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 71.0, 81.0, 101.0, 1001.0, …</pluralRule>
<pluralRule count="few">n % 10 = 2..4 and n % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, … @decimal 2.0, 3.0, 4.0, 22.0, 23.0, 24.0, 32.0, 33.0, 102.0, 1002.0, …</pluralRule>
<pluralRule count="many">n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
<pluralRule count="other"> @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.1, 1000.1, …</pluralRule>
</pluralRules>
<pluralRules locales="lt">
<pluralRule count="one">n % 10 = 1 and n % 100 != 11..19 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 71.0, 81.0, 101.0, 1001.0, …</pluralRule>
<pluralRule count="few">n % 10 = 2..9 and n % 100 != 11..19 @integer 2~9, 22~29, 102, 1002, … @decimal 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 22.0, 102.0, 1002.0, …</pluralRule>
<pluralRule count="many">f != 0 @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.1, 1000.1, …</pluralRule>
<pluralRule count="other"> @integer 0, 10~20, 30, 40, 50, 60, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="mt">
<pluralRule count="one">n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000</pluralRule>
<pluralRule count="few">n = 0 or n % 100 = 2..10 @integer 0, 2~10, 102~107, 1002, … @decimal 0.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 10.0, 102.0, 1002.0, …</pluralRule>
<pluralRule count="many">n % 100 = 11..19 @integer 11~19, 111~117, 1011, … @decimal 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 111.0, 1011.0, …</pluralRule>
<pluralRule count="other"> @integer 20~35, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="ru uk">
<pluralRule count="one">v = 0 and i % 10 = 1 and i % 100 != 11 @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …</pluralRule>
<pluralRule count="few">v = 0 and i % 10 = 2..4 and i % 100 != 12..14 @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …</pluralRule>
<pluralRule count="many">v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14 @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …</pluralRule>
<pluralRule count="other"> @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<!-- 5: one,two,few,many,other -->
<pluralRules locales="br">
<pluralRule count="one">n % 10 = 1 and n % 100 != 11,71,91 @integer 1, 21, 31, 41, 51, 61, 81, 101, 1001, … @decimal 1.0, 21.0, 31.0, 41.0, 51.0, 61.0, 81.0, 101.0, 1001.0, …</pluralRule>
<pluralRule count="two">n % 10 = 2 and n % 100 != 12,72,92 @integer 2, 22, 32, 42, 52, 62, 82, 102, 1002, … @decimal 2.0, 22.0, 32.0, 42.0, 52.0, 62.0, 82.0, 102.0, 1002.0, …</pluralRule>
<pluralRule count="few">n % 10 = 3..4,9 and n % 100 != 10..19,70..79,90..99 @integer 3, 4, 9, 23, 24, 29, 33, 34, 39, 43, 44, 49, 103, 1003, … @decimal 3.0, 4.0, 9.0, 23.0, 24.0, 29.0, 33.0, 34.0, 103.0, 1003.0, …</pluralRule>
<pluralRule count="many">n != 0 and n % 1000000 = 0 @integer 1000000, … @decimal 1000000.0, 1000000.00, 1000000.000, …</pluralRule>
<pluralRule count="other"> @integer 0, 5~8, 10~20, 100, 1000, 10000, 100000, … @decimal 0.0~0.9, 1.1~1.6, 10.0, 100.0, 1000.0, 10000.0, 100000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="ga">
<pluralRule count="one">n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000</pluralRule>
<pluralRule count="two">n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000</pluralRule>
<pluralRule count="few">n = 3..6 @integer 3~6 @decimal 3.0, 4.0, 5.0, 6.0, 3.00, 4.00, 5.00, 6.00, 3.000, 4.000, 5.000, 6.000, 3.0000, 4.0000, 5.0000, 6.0000</pluralRule>
<pluralRule count="many">n = 7..10 @integer 7~10 @decimal 7.0, 8.0, 9.0, 10.0, 7.00, 8.00, 9.00, 10.00, 7.000, 8.000, 9.000, 10.000, 7.0000, 8.0000, 9.0000, 10.0000</pluralRule>
<pluralRule count="other"> @integer 0, 11~25, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~0.9, 1.1~1.6, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="gv">
<pluralRule count="one">v = 0 and i % 10 = 1 @integer 1, 11, 21, 31, 41, 51, 61, 71, 101, 1001, …</pluralRule>
<pluralRule count="two">v = 0 and i % 10 = 2 @integer 2, 12, 22, 32, 42, 52, 62, 72, 102, 1002, …</pluralRule>
<pluralRule count="few">v = 0 and i % 100 = 0,20,40,60,80 @integer 0, 20, 40, 60, 80, 100, 120, 140, 1000, 10000, 100000, 1000000, …</pluralRule>
<pluralRule count="many">v != 0 @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
<pluralRule count="other"> @integer 3~10, 13~19, 23, 103, 1003, …</pluralRule>
</pluralRules>
<!-- 6: zero,one,two,few,many,other -->
<pluralRules locales="ar">
<pluralRule count="zero">n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000</pluralRule>
<pluralRule count="one">n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000</pluralRule>
<pluralRule count="two">n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000</pluralRule>
<pluralRule count="few">n % 100 = 3..10 @integer 3~10, 103~110, 1003, … @decimal 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 103.0, 1003.0, …</pluralRule>
<pluralRule count="many">n % 100 = 11..99 @integer 11~26, 111, 1011, … @decimal 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 111.0, 1011.0, …</pluralRule>
<pluralRule count="other"> @integer 100~102, 200~202, 300~302, 400~402, 500~502, 600, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.1, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
<pluralRules locales="cy">
<pluralRule count="zero">n = 0 @integer 0 @decimal 0.0, 0.00, 0.000, 0.0000</pluralRule>
<pluralRule count="one">n = 1 @integer 1 @decimal 1.0, 1.00, 1.000, 1.0000</pluralRule>
<pluralRule count="two">n = 2 @integer 2 @decimal 2.0, 2.00, 2.000, 2.0000</pluralRule>
<pluralRule count="few">n = 3 @integer 3 @decimal 3.0, 3.00, 3.000, 3.0000</pluralRule>
<pluralRule count="many">n = 6 @integer 6 @decimal 6.0, 6.00, 6.000, 6.0000</pluralRule>
<pluralRule count="other"> @integer 4, 5, 7~20, 100, 1000, 10000, 100000, 1000000, … @decimal 0.1~0.9, 1.1~1.7, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …</pluralRule>
</pluralRules>
</plurals>
</supplementalData>

143
Godeps/_workspace/src/github.com/nicksnyder/go-i18n/i18n/language/codegen/xml.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,143 @@
package main
import (
"encoding/xml"
"fmt"
"regexp"
"strings"
)
// SupplementalData is the top level struct of plural.xml
type SupplementalData struct {
XMLName xml.Name `xml:"supplementalData"`
PluralGroups []PluralGroup `xml:"plurals>pluralRules"`
}
// PluralGroup is a group of locales with the same plural rules.
type PluralGroup struct {
Locales string `xml:"locales,attr"`
PluralRules []PluralRule `xml:"pluralRule"`
}
// Name returns a unique name for this plural group.
func (pg *PluralGroup) Name() string {
n := strings.Title(pg.Locales)
return strings.Replace(n, " ", "", -1)
}
// SplitLocales returns all the locales in the PluralGroup as a slice.
func (pg *PluralGroup) SplitLocales() []string {
return strings.Split(pg.Locales, " ")
}
// PluralRule is the rule for a single plural form.
type PluralRule struct {
Count string `xml:"count,attr"`
Rule string `xml:",innerxml"`
}
// CountTitle returns the title case of the PluralRule's count.
func (pr *PluralRule) CountTitle() string {
return strings.Title(pr.Count)
}
// Condition returns the condition where the PluralRule applies.
func (pr *PluralRule) Condition() string {
i := strings.Index(pr.Rule, "@")
return pr.Rule[:i]
}
// Examples returns the integer and decimal exmaples for the PLuralRule.
func (pr *PluralRule) Examples() (integer []string, decimal []string) {
ex := strings.Replace(pr.Rule, ", …", "", -1)
ddelim := "@decimal"
if i := strings.Index(ex, ddelim); i > 0 {
dex := strings.TrimSpace(ex[i+len(ddelim):])
decimal = strings.Split(dex, ", ")
ex = ex[:i]
}
idelim := "@integer"
if i := strings.Index(ex, idelim); i > 0 {
iex := strings.TrimSpace(ex[i+len(idelim):])
integer = strings.Split(iex, ", ")
}
return integer, decimal
}
// IntegerExamples returns the integer exmaples for the PLuralRule.
func (pr *PluralRule) IntegerExamples() []string {
integer, _ := pr.Examples()
return integer
}
// DecimalExamples returns the decimal exmaples for the PLuralRule.
func (pr *PluralRule) DecimalExamples() []string {
_, decimal := pr.Examples()
return decimal
}
var relationRegexp = regexp.MustCompile("([niftvw])(?: % ([0-9]+))? (!=|=)(.*)")
// GoCondition converts the XML condition to valid Go code.
func (pr *PluralRule) GoCondition() string {
var ors []string
for _, and := range strings.Split(pr.Condition(), "or") {
var ands []string
for _, relation := range strings.Split(and, "and") {
parts := relationRegexp.FindStringSubmatch(relation)
if parts == nil {
continue
}
lvar, lmod, op, rhs := strings.Title(parts[1]), parts[2], parts[3], strings.TrimSpace(parts[4])
if op == "=" {
op = "=="
}
lvar = "ops." + lvar
var rhor []string
var rany []string
for _, rh := range strings.Split(rhs, ",") {
if parts := strings.Split(rh, ".."); len(parts) == 2 {
from, to := parts[0], parts[1]
if lvar == "ops.N" {
if lmod != "" {
rhor = append(rhor, fmt.Sprintf("ops.NmodInRange(%s, %s, %s)", lmod, from, to))
} else {
rhor = append(rhor, fmt.Sprintf("ops.NinRange(%s, %s)", from, to))
}
} else if lmod != "" {
rhor = append(rhor, fmt.Sprintf("intInRange(%s %% %s, %s, %s)", lvar, lmod, from, to))
} else {
rhor = append(rhor, fmt.Sprintf("intInRange(%s, %s, %s)", lvar, from, to))
}
} else {
rany = append(rany, rh)
}
}
if len(rany) > 0 {
rh := strings.Join(rany, ",")
if lvar == "ops.N" {
if lmod != "" {
rhor = append(rhor, fmt.Sprintf("ops.NmodEqualsAny(%s, %s)", lmod, rh))
} else {
rhor = append(rhor, fmt.Sprintf("ops.NequalsAny(%s)", rh))
}
} else if lmod != "" {
rhor = append(rhor, fmt.Sprintf("intEqualsAny(%s %% %s, %s)", lvar, lmod, rh))
} else {
rhor = append(rhor, fmt.Sprintf("intEqualsAny(%s, %s)", lvar, rh))
}
}
r := strings.Join(rhor, " || ")
if len(rhor) > 1 {
r = "(" + r + ")"
}
if op == "!=" {
r = "!" + r
}
ands = append(ands, r)
}
ors = append(ors, strings.Join(ands, " && "))
}
return strings.Join(ors, " ||\n")
}

99
Godeps/_workspace/src/github.com/nicksnyder/go-i18n/i18n/language/language.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,99 @@
// Package language defines languages that implement CLDR pluralization.
package language
import (
"fmt"
"strings"
)
// Language is a written human language.
type Language struct {
// Tag uniquely identifies the language as defined by RFC 5646.
//
// Most language tags are a two character language code (ISO 639-1)
// optionally followed by a dash and a two character country code (ISO 3166-1).
// (e.g. en, pt-br)
Tag string
*PluralSpec
}
func (l *Language) String() string {
return l.Tag
}
// MatchingTags returns the set of language tags that map to this Language.
// e.g. "zh-hans-cn" yields {"zh", "zh-hans", "zh-hans-cn"}
// BUG: This should be computed once and stored as a field on Language for efficiency,
// but this would require changing how Languages are constructed.
func (l *Language) MatchingTags() []string {
parts := strings.Split(l.Tag, "-")
var prefix, matches []string
for _, part := range parts {
prefix = append(prefix, part)
match := strings.Join(prefix, "-")
matches = append(matches, match)
}
return matches
}
// Parse returns a slice of supported languages found in src or nil if none are found.
// It can parse language tags and Accept-Language headers.
func Parse(src string) []*Language {
var langs []*Language
start := 0
for end, chr := range src {
switch chr {
case ',', ';', '.':
tag := strings.TrimSpace(src[start:end])
if spec := getPluralSpec(tag); spec != nil {
langs = append(langs, &Language{NormalizeTag(tag), spec})
}
start = end + 1
}
}
if start > 0 {
tag := strings.TrimSpace(src[start:])
if spec := getPluralSpec(tag); spec != nil {
langs = append(langs, &Language{NormalizeTag(tag), spec})
}
return dedupe(langs)
}
if spec := getPluralSpec(src); spec != nil {
langs = append(langs, &Language{NormalizeTag(src), spec})
}
return langs
}
func dedupe(langs []*Language) []*Language {
found := make(map[string]struct{}, len(langs))
deduped := make([]*Language, 0, len(langs))
for _, lang := range langs {
if _, ok := found[lang.Tag]; !ok {
found[lang.Tag] = struct{}{}
deduped = append(deduped, lang)
}
}
return deduped
}
// MustParse is similar to Parse except it panics instead of retuning a nil Language.
func MustParse(src string) []*Language {
langs := Parse(src)
if len(langs) == 0 {
panic(fmt.Errorf("unable to parse language from %q", src))
}
return langs
}
// Add adds support for a new language.
func Add(l *Language) {
tag := NormalizeTag(l.Tag)
pluralSpecs[tag] = l.PluralSpec
}
// NormalizeTag returns a language tag with all lower-case characters
// and dashes "-" instead of underscores "_"
func NormalizeTag(tag string) string {
tag = strings.ToLower(tag)
return strings.Replace(tag, "_", "-", -1)
}

119
Godeps/_workspace/src/github.com/nicksnyder/go-i18n/i18n/language/operands.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,119 @@
package language
import (
"fmt"
"strconv"
"strings"
)
// http://unicode.org/reports/tr35/tr35-numbers.html#Operands
type operands struct {
N float64 // absolute value of the source number (integer and decimals)
I int64 // integer digits of n
V int64 // number of visible fraction digits in n, with trailing zeros
W int64 // number of visible fraction digits in n, without trailing zeros
F int64 // visible fractional digits in n, with trailing zeros
T int64 // visible fractional digits in n, without trailing zeros
}
// NmodEqualAny returns true if o represents an integer equal to any of the arguments.
func (o *operands) NequalsAny(any ...int64) bool {
for _, i := range any {
if o.I == i && o.T == 0 {
return true
}
}
return false
}
// NmodEqualAny returns true if o represents an integer equal to any of the arguments modulo mod.
func (o *operands) NmodEqualsAny(mod int64, any ...int64) bool {
modI := o.I % mod
for _, i := range any {
if modI == i && o.T == 0 {
return true
}
}
return false
}
// NmodInRange returns true if o represents an integer in the closed interval [from, to].
func (o *operands) NinRange(from, to int64) bool {
return o.T == 0 && from <= o.I && o.I <= to
}
// NmodInRange returns true if o represents an integer in the closed interval [from, to] modulo mod.
func (o *operands) NmodInRange(mod, from, to int64) bool {
modI := o.I % mod
return o.T == 0 && from <= modI && modI <= to
}
func newOperands(v interface{}) (*operands, error) {
switch v := v.(type) {
case int:
return newOperandsInt64(int64(v)), nil
case int8:
return newOperandsInt64(int64(v)), nil
case int16:
return newOperandsInt64(int64(v)), nil
case int32:
return newOperandsInt64(int64(v)), nil
case int64:
return newOperandsInt64(v), nil
case string:
return newOperandsString(v)
case float32, float64:
return nil, fmt.Errorf("floats should be formatted into a string")
default:
return nil, fmt.Errorf("invalid type %T; expected integer or string", v)
}
}
func newOperandsInt64(i int64) *operands {
if i < 0 {
i = -i
}
return &operands{float64(i), i, 0, 0, 0, 0}
}
func newOperandsString(s string) (*operands, error) {
if s[0] == '-' {
s = s[1:]
}
n, err := strconv.ParseFloat(s, 64)
if err != nil {
return nil, err
}
ops := &operands{N: n}
parts := strings.SplitN(s, ".", 2)
ops.I, err = strconv.ParseInt(parts[0], 10, 64)
if err != nil {
return nil, err
}
if len(parts) == 1 {
return ops, nil
}
fraction := parts[1]
ops.V = int64(len(fraction))
for i := ops.V - 1; i >= 0; i-- {
if fraction[i] != '0' {
ops.W = i + 1
break
}
}
if ops.V > 0 {
f, err := strconv.ParseInt(fraction, 10, 0)
if err != nil {
return nil, err
}
ops.F = f
}
if ops.W > 0 {
t, err := strconv.ParseInt(fraction[:ops.W], 10, 0)
if err != nil {
return nil, err
}
ops.T = t
}
return ops, nil
}

40
Godeps/_workspace/src/github.com/nicksnyder/go-i18n/i18n/language/plural.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,40 @@
package language
import (
"fmt"
)
// Plural represents a language pluralization form as defined here:
// http://cldr.unicode.org/index/cldr-spec/plural-rules
type Plural string
// All defined plural categories.
const (
Invalid Plural = "invalid"
Zero = "zero"
One = "one"
Two = "two"
Few = "few"
Many = "many"
Other = "other"
)
// NewPlural returns src as a Plural
// or Invalid and a non-nil error if src is not a valid Plural.
func NewPlural(src string) (Plural, error) {
switch src {
case "zero":
return Zero, nil
case "one":
return One, nil
case "two":
return Two, nil
case "few":
return Few, nil
case "many":
return Many, nil
case "other":
return Other, nil
}
return Invalid, fmt.Errorf("invalid plural category %s", src)
}

74
Godeps/_workspace/src/github.com/nicksnyder/go-i18n/i18n/language/pluralspec.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,74 @@
package language
import "strings"
// PluralSpec defines the CLDR plural rules for a language.
// http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
// http://unicode.org/reports/tr35/tr35-numbers.html#Operands
type PluralSpec struct {
Plurals map[Plural]struct{}
PluralFunc func(*operands) Plural
}
var pluralSpecs = make(map[string]*PluralSpec)
func normalizePluralSpecID(id string) string {
id = strings.Replace(id, "_", "-", -1)
id = strings.ToLower(id)
return id
}
func registerPluralSpec(ids []string, ps *PluralSpec) {
for _, id := range ids {
id = normalizePluralSpecID(id)
pluralSpecs[id] = ps
}
}
// Plural returns the plural category for number as defined by
// the language's CLDR plural rules.
func (ps *PluralSpec) Plural(number interface{}) (Plural, error) {
ops, err := newOperands(number)
if err != nil {
return Invalid, err
}
return ps.PluralFunc(ops), nil
}
// getPluralSpec returns the PluralSpec that matches the longest prefix of tag.
// It returns nil if no PluralSpec matches tag.
func getPluralSpec(tag string) *PluralSpec {
tag = NormalizeTag(tag)
subtag := tag
for {
if spec := pluralSpecs[subtag]; spec != nil {
return spec
}
end := strings.LastIndex(subtag, "-")
if end == -1 {
return nil
}
subtag = subtag[:end]
}
}
func newPluralSet(plurals ...Plural) map[Plural]struct{} {
set := make(map[Plural]struct{}, len(plurals))
for _, plural := range plurals {
set[plural] = struct{}{}
}
return set
}
func intInRange(i, from, to int64) bool {
return from <= i && i <= to
}
func intEqualsAny(i int64, any ...int64) bool {
for _, a := range any {
if i == a {
return true
}
}
return false
}

567
Godeps/_workspace/src/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_gen.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,567 @@
package language
// This file is generated by i18n/language/codegen/generate.sh
func init() {
registerPluralSpec([]string{"bm", "bo", "dz", "id", "ig", "ii", "in", "ja", "jbo", "jv", "jw", "kde", "kea", "km", "ko", "lkt", "lo", "ms", "my", "nqo", "root", "sah", "ses", "sg", "th", "to", "vi", "wo", "yo", "zh"}, &PluralSpec{
Plurals: newPluralSet(Other),
PluralFunc: func(ops *operands) Plural {
return Other
},
})
registerPluralSpec([]string{"am", "as", "bn", "fa", "gu", "hi", "kn", "mr", "zu"}, &PluralSpec{
Plurals: newPluralSet(One, Other),
PluralFunc: func(ops *operands) Plural {
// i = 0 or n = 1
if intEqualsAny(ops.I, 0) ||
ops.NequalsAny(1) {
return One
}
return Other
},
})
registerPluralSpec([]string{"ff", "fr", "hy", "kab"}, &PluralSpec{
Plurals: newPluralSet(One, Other),
PluralFunc: func(ops *operands) Plural {
// i = 0,1
if intEqualsAny(ops.I, 0, 1) {
return One
}
return Other
},
})
registerPluralSpec([]string{"ast", "ca", "de", "en", "et", "fi", "fy", "gl", "it", "ji", "nl", "sv", "sw", "ur", "yi"}, &PluralSpec{
Plurals: newPluralSet(One, Other),
PluralFunc: func(ops *operands) Plural {
// i = 1 and v = 0
if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
return One
}
return Other
},
})
registerPluralSpec([]string{"si"}, &PluralSpec{
Plurals: newPluralSet(One, Other),
PluralFunc: func(ops *operands) Plural {
// n = 0,1 or i = 0 and f = 1
if ops.NequalsAny(0, 1) ||
intEqualsAny(ops.I, 0) && intEqualsAny(ops.F, 1) {
return One
}
return Other
},
})
registerPluralSpec([]string{"ak", "bh", "guw", "ln", "mg", "nso", "pa", "ti", "wa"}, &PluralSpec{
Plurals: newPluralSet(One, Other),
PluralFunc: func(ops *operands) Plural {
// n = 0..1
if ops.NinRange(0, 1) {
return One
}
return Other
},
})
registerPluralSpec([]string{"tzm"}, &PluralSpec{
Plurals: newPluralSet(One, Other),
PluralFunc: func(ops *operands) Plural {
// n = 0..1 or n = 11..99
if ops.NinRange(0, 1) ||
ops.NinRange(11, 99) {
return One
}
return Other
},
})
registerPluralSpec([]string{"pt"}, &PluralSpec{
Plurals: newPluralSet(One, Other),
PluralFunc: func(ops *operands) Plural {
// n = 0..2 and n != 2
if ops.NinRange(0, 2) && !ops.NequalsAny(2) {
return One
}
return Other
},
})
registerPluralSpec([]string{"af", "asa", "az", "bem", "bez", "bg", "brx", "ce", "cgg", "chr", "ckb", "dv", "ee", "el", "eo", "es", "eu", "fo", "fur", "gsw", "ha", "haw", "hu", "jgo", "jmc", "ka", "kaj", "kcg", "kk", "kkj", "kl", "ks", "ksb", "ku", "ky", "lb", "lg", "mas", "mgo", "ml", "mn", "nah", "nb", "nd", "ne", "nn", "nnh", "no", "nr", "ny", "nyn", "om", "or", "os", "pap", "ps", "rm", "rof", "rwk", "saq", "sdh", "seh", "sn", "so", "sq", "ss", "ssy", "st", "syr", "ta", "te", "teo", "tig", "tk", "tn", "tr", "ts", "ug", "uz", "ve", "vo", "vun", "wae", "xh", "xog"}, &PluralSpec{
Plurals: newPluralSet(One, Other),
PluralFunc: func(ops *operands) Plural {
// n = 1
if ops.NequalsAny(1) {
return One
}
return Other
},
})
registerPluralSpec([]string{"pt_PT"}, &PluralSpec{
Plurals: newPluralSet(One, Other),
PluralFunc: func(ops *operands) Plural {
// n = 1 and v = 0
if ops.NequalsAny(1) && intEqualsAny(ops.V, 0) {
return One
}
return Other
},
})
registerPluralSpec([]string{"da"}, &PluralSpec{
Plurals: newPluralSet(One, Other),
PluralFunc: func(ops *operands) Plural {
// n = 1 or t != 0 and i = 0,1
if ops.NequalsAny(1) ||
!intEqualsAny(ops.T, 0) && intEqualsAny(ops.I, 0, 1) {
return One
}
return Other
},
})
registerPluralSpec([]string{"is"}, &PluralSpec{
Plurals: newPluralSet(One, Other),
PluralFunc: func(ops *operands) Plural {
// t = 0 and i % 10 = 1 and i % 100 != 11 or t != 0
if intEqualsAny(ops.T, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) ||
!intEqualsAny(ops.T, 0) {
return One
}
return Other
},
})
registerPluralSpec([]string{"mk"}, &PluralSpec{
Plurals: newPluralSet(One, Other),
PluralFunc: func(ops *operands) Plural {
// v = 0 and i % 10 = 1 or f % 10 = 1
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) ||
intEqualsAny(ops.F%10, 1) {
return One
}
return Other
},
})
registerPluralSpec([]string{"fil", "tl"}, &PluralSpec{
Plurals: newPluralSet(One, Other),
PluralFunc: func(ops *operands) Plural {
// v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I, 1, 2, 3) ||
intEqualsAny(ops.V, 0) && !intEqualsAny(ops.I%10, 4, 6, 9) ||
!intEqualsAny(ops.V, 0) && !intEqualsAny(ops.F%10, 4, 6, 9) {
return One
}
return Other
},
})
registerPluralSpec([]string{"lv", "prg"}, &PluralSpec{
Plurals: newPluralSet(Zero, One, Other),
PluralFunc: func(ops *operands) Plural {
// n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19
if ops.NmodEqualsAny(10, 0) ||
ops.NmodInRange(100, 11, 19) ||
intEqualsAny(ops.V, 2) && intInRange(ops.F%100, 11, 19) {
return Zero
}
// n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1
if ops.NmodEqualsAny(10, 1) && !ops.NmodEqualsAny(100, 11) ||
intEqualsAny(ops.V, 2) && intEqualsAny(ops.F%10, 1) && !intEqualsAny(ops.F%100, 11) ||
!intEqualsAny(ops.V, 2) && intEqualsAny(ops.F%10, 1) {
return One
}
return Other
},
})
registerPluralSpec([]string{"lag"}, &PluralSpec{
Plurals: newPluralSet(Zero, One, Other),
PluralFunc: func(ops *operands) Plural {
// n = 0
if ops.NequalsAny(0) {
return Zero
}
// i = 0,1 and n != 0
if intEqualsAny(ops.I, 0, 1) && !ops.NequalsAny(0) {
return One
}
return Other
},
})
registerPluralSpec([]string{"ksh"}, &PluralSpec{
Plurals: newPluralSet(Zero, One, Other),
PluralFunc: func(ops *operands) Plural {
// n = 0
if ops.NequalsAny(0) {
return Zero
}
// n = 1
if ops.NequalsAny(1) {
return One
}
return Other
},
})
registerPluralSpec([]string{"iu", "kw", "naq", "se", "sma", "smi", "smj", "smn", "sms"}, &PluralSpec{
Plurals: newPluralSet(One, Two, Other),
PluralFunc: func(ops *operands) Plural {
// n = 1
if ops.NequalsAny(1) {
return One
}
// n = 2
if ops.NequalsAny(2) {
return Two
}
return Other
},
})
registerPluralSpec([]string{"shi"}, &PluralSpec{
Plurals: newPluralSet(One, Few, Other),
PluralFunc: func(ops *operands) Plural {
// i = 0 or n = 1
if intEqualsAny(ops.I, 0) ||
ops.NequalsAny(1) {
return One
}
// n = 2..10
if ops.NinRange(2, 10) {
return Few
}
return Other
},
})
registerPluralSpec([]string{"mo", "ro"}, &PluralSpec{
Plurals: newPluralSet(One, Few, Other),
PluralFunc: func(ops *operands) Plural {
// i = 1 and v = 0
if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
return One
}
// v != 0 or n = 0 or n != 1 and n % 100 = 1..19
if !intEqualsAny(ops.V, 0) ||
ops.NequalsAny(0) ||
!ops.NequalsAny(1) && ops.NmodInRange(100, 1, 19) {
return Few
}
return Other
},
})
registerPluralSpec([]string{"bs", "hr", "sh", "sr"}, &PluralSpec{
Plurals: newPluralSet(One, Few, Other),
PluralFunc: func(ops *operands) Plural {
// v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) ||
intEqualsAny(ops.F%10, 1) && !intEqualsAny(ops.F%100, 11) {
return One
}
// v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14
if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) ||
intInRange(ops.F%10, 2, 4) && !intInRange(ops.F%100, 12, 14) {
return Few
}
return Other
},
})
registerPluralSpec([]string{"gd"}, &PluralSpec{
Plurals: newPluralSet(One, Two, Few, Other),
PluralFunc: func(ops *operands) Plural {
// n = 1,11
if ops.NequalsAny(1, 11) {
return One
}
// n = 2,12
if ops.NequalsAny(2, 12) {
return Two
}
// n = 3..10,13..19
if ops.NinRange(3, 10) || ops.NinRange(13, 19) {
return Few
}
return Other
},
})
registerPluralSpec([]string{"sl"}, &PluralSpec{
Plurals: newPluralSet(One, Two, Few, Other),
PluralFunc: func(ops *operands) Plural {
// v = 0 and i % 100 = 1
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 1) {
return One
}
// v = 0 and i % 100 = 2
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 2) {
return Two
}
// v = 0 and i % 100 = 3..4 or v != 0
if intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 3, 4) ||
!intEqualsAny(ops.V, 0) {
return Few
}
return Other
},
})
registerPluralSpec([]string{"dsb", "hsb"}, &PluralSpec{
Plurals: newPluralSet(One, Two, Few, Other),
PluralFunc: func(ops *operands) Plural {
// v = 0 and i % 100 = 1 or f % 100 = 1
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 1) ||
intEqualsAny(ops.F%100, 1) {
return One
}
// v = 0 and i % 100 = 2 or f % 100 = 2
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 2) ||
intEqualsAny(ops.F%100, 2) {
return Two
}
// v = 0 and i % 100 = 3..4 or f % 100 = 3..4
if intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 3, 4) ||
intInRange(ops.F%100, 3, 4) {
return Few
}
return Other
},
})
registerPluralSpec([]string{"he", "iw"}, &PluralSpec{
Plurals: newPluralSet(One, Two, Many, Other),
PluralFunc: func(ops *operands) Plural {
// i = 1 and v = 0
if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
return One
}
// i = 2 and v = 0
if intEqualsAny(ops.I, 2) && intEqualsAny(ops.V, 0) {
return Two
}
// v = 0 and n != 0..10 and n % 10 = 0
if intEqualsAny(ops.V, 0) && !ops.NinRange(0, 10) && ops.NmodEqualsAny(10, 0) {
return Many
}
return Other
},
})
registerPluralSpec([]string{"cs", "sk"}, &PluralSpec{
Plurals: newPluralSet(One, Few, Many, Other),
PluralFunc: func(ops *operands) Plural {
// i = 1 and v = 0
if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
return One
}
// i = 2..4 and v = 0
if intInRange(ops.I, 2, 4) && intEqualsAny(ops.V, 0) {
return Few
}
// v != 0
if !intEqualsAny(ops.V, 0) {
return Many
}
return Other
},
})
registerPluralSpec([]string{"pl"}, &PluralSpec{
Plurals: newPluralSet(One, Few, Many, Other),
PluralFunc: func(ops *operands) Plural {
// i = 1 and v = 0
if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
return One
}
// v = 0 and i % 10 = 2..4 and i % 100 != 12..14
if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) {
return Few
}
// v = 0 and i != 1 and i % 10 = 0..1 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14
if intEqualsAny(ops.V, 0) && !intEqualsAny(ops.I, 1) && intInRange(ops.I%10, 0, 1) ||
intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 5, 9) ||
intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 12, 14) {
return Many
}
return Other
},
})
registerPluralSpec([]string{"be"}, &PluralSpec{
Plurals: newPluralSet(One, Few, Many, Other),
PluralFunc: func(ops *operands) Plural {
// n % 10 = 1 and n % 100 != 11
if ops.NmodEqualsAny(10, 1) && !ops.NmodEqualsAny(100, 11) {
return One
}
// n % 10 = 2..4 and n % 100 != 12..14
if ops.NmodInRange(10, 2, 4) && !ops.NmodInRange(100, 12, 14) {
return Few
}
// n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14
if ops.NmodEqualsAny(10, 0) ||
ops.NmodInRange(10, 5, 9) ||
ops.NmodInRange(100, 11, 14) {
return Many
}
return Other
},
})
registerPluralSpec([]string{"lt"}, &PluralSpec{
Plurals: newPluralSet(One, Few, Many, Other),
PluralFunc: func(ops *operands) Plural {
// n % 10 = 1 and n % 100 != 11..19
if ops.NmodEqualsAny(10, 1) && !ops.NmodInRange(100, 11, 19) {
return One
}
// n % 10 = 2..9 and n % 100 != 11..19
if ops.NmodInRange(10, 2, 9) && !ops.NmodInRange(100, 11, 19) {
return Few
}
// f != 0
if !intEqualsAny(ops.F, 0) {
return Many
}
return Other
},
})
registerPluralSpec([]string{"mt"}, &PluralSpec{
Plurals: newPluralSet(One, Few, Many, Other),
PluralFunc: func(ops *operands) Plural {
// n = 1
if ops.NequalsAny(1) {
return One
}
// n = 0 or n % 100 = 2..10
if ops.NequalsAny(0) ||
ops.NmodInRange(100, 2, 10) {
return Few
}
// n % 100 = 11..19
if ops.NmodInRange(100, 11, 19) {
return Many
}
return Other
},
})
registerPluralSpec([]string{"ru", "uk"}, &PluralSpec{
Plurals: newPluralSet(One, Few, Many, Other),
PluralFunc: func(ops *operands) Plural {
// v = 0 and i % 10 = 1 and i % 100 != 11
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) {
return One
}
// v = 0 and i % 10 = 2..4 and i % 100 != 12..14
if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) {
return Few
}
// v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 0) ||
intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 5, 9) ||
intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 11, 14) {
return Many
}
return Other
},
})
registerPluralSpec([]string{"br"}, &PluralSpec{
Plurals: newPluralSet(One, Two, Few, Many, Other),
PluralFunc: func(ops *operands) Plural {
// n % 10 = 1 and n % 100 != 11,71,91
if ops.NmodEqualsAny(10, 1) && !ops.NmodEqualsAny(100, 11, 71, 91) {
return One
}
// n % 10 = 2 and n % 100 != 12,72,92
if ops.NmodEqualsAny(10, 2) && !ops.NmodEqualsAny(100, 12, 72, 92) {
return Two
}
// n % 10 = 3..4,9 and n % 100 != 10..19,70..79,90..99
if (ops.NmodInRange(10, 3, 4) || ops.NmodEqualsAny(10, 9)) && !(ops.NmodInRange(100, 10, 19) || ops.NmodInRange(100, 70, 79) || ops.NmodInRange(100, 90, 99)) {
return Few
}
// n != 0 and n % 1000000 = 0
if !ops.NequalsAny(0) && ops.NmodEqualsAny(1000000, 0) {
return Many
}
return Other
},
})
registerPluralSpec([]string{"ga"}, &PluralSpec{
Plurals: newPluralSet(One, Two, Few, Many, Other),
PluralFunc: func(ops *operands) Plural {
// n = 1
if ops.NequalsAny(1) {
return One
}
// n = 2
if ops.NequalsAny(2) {
return Two
}
// n = 3..6
if ops.NinRange(3, 6) {
return Few
}
// n = 7..10
if ops.NinRange(7, 10) {
return Many
}
return Other
},
})
registerPluralSpec([]string{"gv"}, &PluralSpec{
Plurals: newPluralSet(One, Two, Few, Many, Other),
PluralFunc: func(ops *operands) Plural {
// v = 0 and i % 10 = 1
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) {
return One
}
// v = 0 and i % 10 = 2
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 2) {
return Two
}
// v = 0 and i % 100 = 0,20,40,60,80
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 0, 20, 40, 60, 80) {
return Few
}
// v != 0
if !intEqualsAny(ops.V, 0) {
return Many
}
return Other
},
})
registerPluralSpec([]string{"ar"}, &PluralSpec{
Plurals: newPluralSet(Zero, One, Two, Few, Many, Other),
PluralFunc: func(ops *operands) Plural {
// n = 0
if ops.NequalsAny(0) {
return Zero
}
// n = 1
if ops.NequalsAny(1) {
return One
}
// n = 2
if ops.NequalsAny(2) {
return Two
}
// n % 100 = 3..10
if ops.NmodInRange(100, 3, 10) {
return Few
}
// n % 100 = 11..99
if ops.NmodInRange(100, 11, 99) {
return Many
}
return Other
},
})
registerPluralSpec([]string{"cy"}, &PluralSpec{
Plurals: newPluralSet(Zero, One, Two, Few, Many, Other),
PluralFunc: func(ops *operands) Plural {
// n = 0
if ops.NequalsAny(0) {
return Zero
}
// n = 1
if ops.NequalsAny(1) {
return One
}
// n = 2
if ops.NequalsAny(2) {
return Two
}
// n = 3
if ops.NequalsAny(3) {
return Few
}
// n = 6
if ops.NequalsAny(6) {
return Many
}
return Other
},
})
}

78
Godeps/_workspace/src/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,78 @@
package translation
import (
"github.com/nicksnyder/go-i18n/i18n/language"
)
type pluralTranslation struct {
id string
templates map[language.Plural]*template
}
func (pt *pluralTranslation) MarshalInterface() interface{} {
return map[string]interface{}{
"id": pt.id,
"translation": pt.templates,
}
}
func (pt *pluralTranslation) ID() string {
return pt.id
}
func (pt *pluralTranslation) Template(pc language.Plural) *template {
return pt.templates[pc]
}
func (pt *pluralTranslation) UntranslatedCopy() Translation {
return &pluralTranslation{pt.id, make(map[language.Plural]*template)}
}
func (pt *pluralTranslation) Normalize(l *language.Language) Translation {
// Delete plural categories that don't belong to this language.
for pc := range pt.templates {
if _, ok := l.Plurals[pc]; !ok {
delete(pt.templates, pc)
}
}
// Create map entries for missing valid categories.
for pc := range l.Plurals {
if _, ok := pt.templates[pc]; !ok {
pt.templates[pc] = mustNewTemplate("")
}
}
return pt
}
func (pt *pluralTranslation) Backfill(src Translation) Translation {
for pc, t := range pt.templates {
if t == nil || t.src == "" {
pt.templates[pc] = src.Template(language.Other)
}
}
return pt
}
func (pt *pluralTranslation) Merge(t Translation) Translation {
other, ok := t.(*pluralTranslation)
if !ok || pt.ID() != t.ID() {
return t
}
for pluralCategory, template := range other.templates {
if template != nil && template.src != "" {
pt.templates[pluralCategory] = template
}
}
return pt
}
func (pt *pluralTranslation) Incomplete(l *language.Language) bool {
for pc := range l.Plurals {
if t := pt.templates[pc]; t == nil || t.src == "" {
return true
}
}
return false
}
var _ = Translation(&pluralTranslation{})

57
Godeps/_workspace/src/github.com/nicksnyder/go-i18n/i18n/translation/single_translation.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,57 @@
package translation
import (
"github.com/nicksnyder/go-i18n/i18n/language"
)
type singleTranslation struct {
id string
template *template
}
func (st *singleTranslation) MarshalInterface() interface{} {
return map[string]interface{}{
"id": st.id,
"translation": st.template,
}
}
func (st *singleTranslation) ID() string {
return st.id
}
func (st *singleTranslation) Template(pc language.Plural) *template {
return st.template
}
func (st *singleTranslation) UntranslatedCopy() Translation {
return &singleTranslation{st.id, mustNewTemplate("")}
}
func (st *singleTranslation) Normalize(language *language.Language) Translation {
return st
}
func (st *singleTranslation) Backfill(src Translation) Translation {
if st.template == nil || st.template.src == "" {
st.template = src.Template(language.Other)
}
return st
}
func (st *singleTranslation) Merge(t Translation) Translation {
other, ok := t.(*singleTranslation)
if !ok || st.ID() != t.ID() {
return t
}
if other.template != nil && other.template.src != "" {
st.template = other.template
}
return st
}
func (st *singleTranslation) Incomplete(l *language.Language) bool {
return st.template == nil || st.template.src == ""
}
var _ = Translation(&singleTranslation{})

61
Godeps/_workspace/src/github.com/nicksnyder/go-i18n/i18n/translation/template.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,61 @@
package translation
import (
"bytes"
"encoding"
"strings"
gotemplate "text/template"
)
type template struct {
tmpl *gotemplate.Template
src string
}
func newTemplate(src string) (*template, error) {
var tmpl template
err := tmpl.parseTemplate(src)
return &tmpl, err
}
func mustNewTemplate(src string) *template {
t, err := newTemplate(src)
if err != nil {
panic(err)
}
return t
}
func (t *template) String() string {
return t.src
}
func (t *template) Execute(args interface{}) string {
if t.tmpl == nil {
return t.src
}
var buf bytes.Buffer
if err := t.tmpl.Execute(&buf, args); err != nil {
return err.Error()
}
return buf.String()
}
func (t *template) MarshalText() ([]byte, error) {
return []byte(t.src), nil
}
func (t *template) UnmarshalText(src []byte) error {
return t.parseTemplate(string(src))
}
func (t *template) parseTemplate(src string) (err error) {
t.src = src
if strings.Contains(src, "{{") {
t.tmpl, err = gotemplate.New(src).Parse(src)
}
return
}
var _ = encoding.TextMarshaler(&template{})
var _ = encoding.TextUnmarshaler(&template{})

83
Godeps/_workspace/src/github.com/nicksnyder/go-i18n/i18n/translation/translation.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,83 @@
// Package translation defines the interface for a translation.
package translation
import (
"fmt"
"github.com/nicksnyder/go-i18n/i18n/language"
)
// Translation is the interface that represents a translated string.
type Translation interface {
// MarshalInterface returns the object that should be used
// to serialize the translation.
MarshalInterface() interface{}
ID() string
Template(language.Plural) *template
UntranslatedCopy() Translation
Normalize(language *language.Language) Translation
Backfill(src Translation) Translation
Merge(Translation) Translation
Incomplete(l *language.Language) bool
}
// SortableByID implements sort.Interface for a slice of translations.
type SortableByID []Translation
func (a SortableByID) Len() int { return len(a) }
func (a SortableByID) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a SortableByID) Less(i, j int) bool { return a[i].ID() < a[j].ID() }
// NewTranslation reflects on data to create a new Translation.
//
// data["id"] must be a string and data["translation"] must be either a string
// for a non-plural translation or a map[string]interface{} for a plural translation.
func NewTranslation(data map[string]interface{}) (Translation, error) {
id, ok := data["id"].(string)
if !ok {
return nil, fmt.Errorf(`missing "id" key`)
}
var pluralObject map[string]interface{}
switch translation := data["translation"].(type) {
case string:
tmpl, err := newTemplate(translation)
if err != nil {
return nil, err
}
return &singleTranslation{id, tmpl}, nil
case map[interface{}]interface{}:
// The YAML parser uses interface{} keys so we first convert them to string keys.
pluralObject = make(map[string]interface{})
for k, v := range translation {
kStr, ok := k.(string)
if !ok {
return nil, fmt.Errorf(`invalid plural category type %T; expected string`, k)
}
pluralObject[kStr] = v
}
case map[string]interface{}:
pluralObject = translation
case nil:
return nil, fmt.Errorf(`missing "translation" key`)
default:
return nil, fmt.Errorf(`unsupported type for "translation" key %T`, translation)
}
templates := make(map[language.Plural]*template, len(pluralObject))
for k, v := range pluralObject {
pc, err := language.NewPlural(k)
if err != nil {
return nil, err
}
str, ok := v.(string)
if !ok {
return nil, fmt.Errorf(`plural category "%s" has value of type %T; expected string`, pc, v)
}
tmpl, err := newTemplate(str)
if err != nil {
return nil, err
}
templates[pc] = tmpl
}
return &pluralTranslation{id, templates}, nil
}

188
Godeps/_workspace/src/gopkg.in/yaml.v2/LICENSE сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,188 @@
Copyright (c) 2011-2014 - Canonical Inc.
This software is licensed under the LGPLv3, included below.
As a special exception to the GNU Lesser General Public License version 3
("LGPL3"), the copyright holders of this Library give you permission to
convey to a third party a Combined Work that links statically or dynamically
to this Library without providing any Minimal Corresponding Source or
Minimal Application Code as set out in 4d or providing the installation
information set out in section 4e, provided that you comply with the other
provisions of LGPL3 and provided that you meet, for the Application the
terms and conditions of the license(s) which apply to the Application.
Except as stated in this special exception, the provisions of LGPL3 will
continue to comply in full to this Library. If you modify this Library, you
may apply this exception to your version of this Library, but you are not
obliged to do so. If you do not wish to do so, delete this exception
statement from your version. This exception does not (and cannot) modify any
license terms which apply to the Application, with which you must still
comply.
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

31
Godeps/_workspace/src/gopkg.in/yaml.v2/LICENSE.libyaml сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,31 @@
The following files were ported to Go from C files of libyaml, and thus
are still covered by their original copyright and license:
apic.go
emitterc.go
parserc.go
readerc.go
scannerc.go
writerc.go
yamlh.go
yamlprivateh.go
Copyright (c) 2006 Kirill Simonov
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

131
Godeps/_workspace/src/gopkg.in/yaml.v2/README.md сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,131 @@
# YAML support for the Go language
Introduction
------------
The yaml package enables Go programs to comfortably encode and decode YAML
values. It was developed within [Canonical](https://www.canonical.com) as
part of the [juju](https://juju.ubuntu.com) project, and is based on a
pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML)
C library to parse and generate YAML data quickly and reliably.
Compatibility
-------------
The yaml package supports most of YAML 1.1 and 1.2, including support for
anchors, tags, map merging, etc. Multi-document unmarshalling is not yet
implemented, and base-60 floats from YAML 1.1 are purposefully not
supported since they're a poor design and are gone in YAML 1.2.
Installation and usage
----------------------
The import path for the package is *gopkg.in/yaml.v2*.
To install it, run:
go get gopkg.in/yaml.v2
API documentation
-----------------
If opened in a browser, the import path itself leads to the API documentation:
* [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2)
API stability
-------------
The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in).
License
-------
The yaml package is licensed under the LGPL with an exception that allows it to be linked statically. Please see the LICENSE file for details.
Example
-------
```Go
package main
import (
"fmt"
"log"
"gopkg.in/yaml.v2"
)
var data = `
a: Easy!
b:
c: 2
d: [3, 4]
`
type T struct {
A string
B struct {
RenamedC int `yaml:"c"`
D []int `yaml:",flow"`
}
}
func main() {
t := T{}
err := yaml.Unmarshal([]byte(data), &t)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- t:\n%v\n\n", t)
d, err := yaml.Marshal(&t)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- t dump:\n%s\n\n", string(d))
m := make(map[interface{}]interface{})
err = yaml.Unmarshal([]byte(data), &m)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- m:\n%v\n\n", m)
d, err = yaml.Marshal(&m)
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("--- m dump:\n%s\n\n", string(d))
}
```
This example will generate the following output:
```
--- t:
{Easy! {2 [3 4]}}
--- t dump:
a: Easy!
b:
c: 2
d: [3, 4]
--- m:
map[a:Easy! b:map[c:2 d:[3 4]]]
--- m dump:
a: Easy!
b:
c: 2
d:
- 3
- 4
```

742
Godeps/_workspace/src/gopkg.in/yaml.v2/apic.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,742 @@
package yaml
import (
"io"
"os"
)
func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) {
//fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens))
// Check if we can move the queue at the beginning of the buffer.
if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) {
if parser.tokens_head != len(parser.tokens) {
copy(parser.tokens, parser.tokens[parser.tokens_head:])
}
parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head]
parser.tokens_head = 0
}
parser.tokens = append(parser.tokens, *token)
if pos < 0 {
return
}
copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:])
parser.tokens[parser.tokens_head+pos] = *token
}
// Create a new parser object.
func yaml_parser_initialize(parser *yaml_parser_t) bool {
*parser = yaml_parser_t{
raw_buffer: make([]byte, 0, input_raw_buffer_size),
buffer: make([]byte, 0, input_buffer_size),
}
return true
}
// Destroy a parser object.
func yaml_parser_delete(parser *yaml_parser_t) {
*parser = yaml_parser_t{}
}
// String read handler.
func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) {
if parser.input_pos == len(parser.input) {
return 0, io.EOF
}
n = copy(buffer, parser.input[parser.input_pos:])
parser.input_pos += n
return n, nil
}
// File read handler.
func yaml_file_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) {
return parser.input_file.Read(buffer)
}
// Set a string input.
func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) {
if parser.read_handler != nil {
panic("must set the input source only once")
}
parser.read_handler = yaml_string_read_handler
parser.input = input
parser.input_pos = 0
}
// Set a file input.
func yaml_parser_set_input_file(parser *yaml_parser_t, file *os.File) {
if parser.read_handler != nil {
panic("must set the input source only once")
}
parser.read_handler = yaml_file_read_handler
parser.input_file = file
}
// Set the source encoding.
func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) {
if parser.encoding != yaml_ANY_ENCODING {
panic("must set the encoding only once")
}
parser.encoding = encoding
}
// Create a new emitter object.
func yaml_emitter_initialize(emitter *yaml_emitter_t) bool {
*emitter = yaml_emitter_t{
buffer: make([]byte, output_buffer_size),
raw_buffer: make([]byte, 0, output_raw_buffer_size),
states: make([]yaml_emitter_state_t, 0, initial_stack_size),
events: make([]yaml_event_t, 0, initial_queue_size),
}
return true
}
// Destroy an emitter object.
func yaml_emitter_delete(emitter *yaml_emitter_t) {
*emitter = yaml_emitter_t{}
}
// String write handler.
func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
*emitter.output_buffer = append(*emitter.output_buffer, buffer...)
return nil
}
// File write handler.
func yaml_file_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
_, err := emitter.output_file.Write(buffer)
return err
}
// Set a string output.
func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) {
if emitter.write_handler != nil {
panic("must set the output target only once")
}
emitter.write_handler = yaml_string_write_handler
emitter.output_buffer = output_buffer
}
// Set a file output.
func yaml_emitter_set_output_file(emitter *yaml_emitter_t, file io.Writer) {
if emitter.write_handler != nil {
panic("must set the output target only once")
}
emitter.write_handler = yaml_file_write_handler
emitter.output_file = file
}
// Set the output encoding.
func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) {
if emitter.encoding != yaml_ANY_ENCODING {
panic("must set the output encoding only once")
}
emitter.encoding = encoding
}
// Set the canonical output style.
func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) {
emitter.canonical = canonical
}
//// Set the indentation increment.
func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) {
if indent < 2 || indent > 9 {
indent = 2
}
emitter.best_indent = indent
}
// Set the preferred line width.
func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) {
if width < 0 {
width = -1
}
emitter.best_width = width
}
// Set if unescaped non-ASCII characters are allowed.
func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) {
emitter.unicode = unicode
}
// Set the preferred line break character.
func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) {
emitter.line_break = line_break
}
///*
// * Destroy a token object.
// */
//
//YAML_DECLARE(void)
//yaml_token_delete(yaml_token_t *token)
//{
// assert(token); // Non-NULL token object expected.
//
// switch (token.type)
// {
// case YAML_TAG_DIRECTIVE_TOKEN:
// yaml_free(token.data.tag_directive.handle);
// yaml_free(token.data.tag_directive.prefix);
// break;
//
// case YAML_ALIAS_TOKEN:
// yaml_free(token.data.alias.value);
// break;
//
// case YAML_ANCHOR_TOKEN:
// yaml_free(token.data.anchor.value);
// break;
//
// case YAML_TAG_TOKEN:
// yaml_free(token.data.tag.handle);
// yaml_free(token.data.tag.suffix);
// break;
//
// case YAML_SCALAR_TOKEN:
// yaml_free(token.data.scalar.value);
// break;
//
// default:
// break;
// }
//
// memset(token, 0, sizeof(yaml_token_t));
//}
//
///*
// * Check if a string is a valid UTF-8 sequence.
// *
// * Check 'reader.c' for more details on UTF-8 encoding.
// */
//
//static int
//yaml_check_utf8(yaml_char_t *start, size_t length)
//{
// yaml_char_t *end = start+length;
// yaml_char_t *pointer = start;
//
// while (pointer < end) {
// unsigned char octet;
// unsigned int width;
// unsigned int value;
// size_t k;
//
// octet = pointer[0];
// width = (octet & 0x80) == 0x00 ? 1 :
// (octet & 0xE0) == 0xC0 ? 2 :
// (octet & 0xF0) == 0xE0 ? 3 :
// (octet & 0xF8) == 0xF0 ? 4 : 0;
// value = (octet & 0x80) == 0x00 ? octet & 0x7F :
// (octet & 0xE0) == 0xC0 ? octet & 0x1F :
// (octet & 0xF0) == 0xE0 ? octet & 0x0F :
// (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
// if (!width) return 0;
// if (pointer+width > end) return 0;
// for (k = 1; k < width; k ++) {
// octet = pointer[k];
// if ((octet & 0xC0) != 0x80) return 0;
// value = (value << 6) + (octet & 0x3F);
// }
// if (!((width == 1) ||
// (width == 2 && value >= 0x80) ||
// (width == 3 && value >= 0x800) ||
// (width == 4 && value >= 0x10000))) return 0;
//
// pointer += width;
// }
//
// return 1;
//}
//
// Create STREAM-START.
func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) bool {
*event = yaml_event_t{
typ: yaml_STREAM_START_EVENT,
encoding: encoding,
}
return true
}
// Create STREAM-END.
func yaml_stream_end_event_initialize(event *yaml_event_t) bool {
*event = yaml_event_t{
typ: yaml_STREAM_END_EVENT,
}
return true
}
// Create DOCUMENT-START.
func yaml_document_start_event_initialize(event *yaml_event_t, version_directive *yaml_version_directive_t,
tag_directives []yaml_tag_directive_t, implicit bool) bool {
*event = yaml_event_t{
typ: yaml_DOCUMENT_START_EVENT,
version_directive: version_directive,
tag_directives: tag_directives,
implicit: implicit,
}
return true
}
// Create DOCUMENT-END.
func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) bool {
*event = yaml_event_t{
typ: yaml_DOCUMENT_END_EVENT,
implicit: implicit,
}
return true
}
///*
// * Create ALIAS.
// */
//
//YAML_DECLARE(int)
//yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t)
//{
// mark yaml_mark_t = { 0, 0, 0 }
// anchor_copy *yaml_char_t = NULL
//
// assert(event) // Non-NULL event object is expected.
// assert(anchor) // Non-NULL anchor is expected.
//
// if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0
//
// anchor_copy = yaml_strdup(anchor)
// if (!anchor_copy)
// return 0
//
// ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark)
//
// return 1
//}
// Create SCALAR.
func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool {
*event = yaml_event_t{
typ: yaml_SCALAR_EVENT,
anchor: anchor,
tag: tag,
value: value,
implicit: plain_implicit,
quoted_implicit: quoted_implicit,
style: yaml_style_t(style),
}
return true
}
// Create SEQUENCE-START.
func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool {
*event = yaml_event_t{
typ: yaml_SEQUENCE_START_EVENT,
anchor: anchor,
tag: tag,
implicit: implicit,
style: yaml_style_t(style),
}
return true
}
// Create SEQUENCE-END.
func yaml_sequence_end_event_initialize(event *yaml_event_t) bool {
*event = yaml_event_t{
typ: yaml_SEQUENCE_END_EVENT,
}
return true
}
// Create MAPPING-START.
func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) bool {
*event = yaml_event_t{
typ: yaml_MAPPING_START_EVENT,
anchor: anchor,
tag: tag,
implicit: implicit,
style: yaml_style_t(style),
}
return true
}
// Create MAPPING-END.
func yaml_mapping_end_event_initialize(event *yaml_event_t) bool {
*event = yaml_event_t{
typ: yaml_MAPPING_END_EVENT,
}
return true
}
// Destroy an event object.
func yaml_event_delete(event *yaml_event_t) {
*event = yaml_event_t{}
}
///*
// * Create a document object.
// */
//
//YAML_DECLARE(int)
//yaml_document_initialize(document *yaml_document_t,
// version_directive *yaml_version_directive_t,
// tag_directives_start *yaml_tag_directive_t,
// tag_directives_end *yaml_tag_directive_t,
// start_implicit int, end_implicit int)
//{
// struct {
// error yaml_error_type_t
// } context
// struct {
// start *yaml_node_t
// end *yaml_node_t
// top *yaml_node_t
// } nodes = { NULL, NULL, NULL }
// version_directive_copy *yaml_version_directive_t = NULL
// struct {
// start *yaml_tag_directive_t
// end *yaml_tag_directive_t
// top *yaml_tag_directive_t
// } tag_directives_copy = { NULL, NULL, NULL }
// value yaml_tag_directive_t = { NULL, NULL }
// mark yaml_mark_t = { 0, 0, 0 }
//
// assert(document) // Non-NULL document object is expected.
// assert((tag_directives_start && tag_directives_end) ||
// (tag_directives_start == tag_directives_end))
// // Valid tag directives are expected.
//
// if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error
//
// if (version_directive) {
// version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t))
// if (!version_directive_copy) goto error
// version_directive_copy.major = version_directive.major
// version_directive_copy.minor = version_directive.minor
// }
//
// if (tag_directives_start != tag_directives_end) {
// tag_directive *yaml_tag_directive_t
// if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE))
// goto error
// for (tag_directive = tag_directives_start
// tag_directive != tag_directives_end; tag_directive ++) {
// assert(tag_directive.handle)
// assert(tag_directive.prefix)
// if (!yaml_check_utf8(tag_directive.handle,
// strlen((char *)tag_directive.handle)))
// goto error
// if (!yaml_check_utf8(tag_directive.prefix,
// strlen((char *)tag_directive.prefix)))
// goto error
// value.handle = yaml_strdup(tag_directive.handle)
// value.prefix = yaml_strdup(tag_directive.prefix)
// if (!value.handle || !value.prefix) goto error
// if (!PUSH(&context, tag_directives_copy, value))
// goto error
// value.handle = NULL
// value.prefix = NULL
// }
// }
//
// DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy,
// tag_directives_copy.start, tag_directives_copy.top,
// start_implicit, end_implicit, mark, mark)
//
// return 1
//
//error:
// STACK_DEL(&context, nodes)
// yaml_free(version_directive_copy)
// while (!STACK_EMPTY(&context, tag_directives_copy)) {
// value yaml_tag_directive_t = POP(&context, tag_directives_copy)
// yaml_free(value.handle)
// yaml_free(value.prefix)
// }
// STACK_DEL(&context, tag_directives_copy)
// yaml_free(value.handle)
// yaml_free(value.prefix)
//
// return 0
//}
//
///*
// * Destroy a document object.
// */
//
//YAML_DECLARE(void)
//yaml_document_delete(document *yaml_document_t)
//{
// struct {
// error yaml_error_type_t
// } context
// tag_directive *yaml_tag_directive_t
//
// context.error = YAML_NO_ERROR // Eliminate a compliler warning.
//
// assert(document) // Non-NULL document object is expected.
//
// while (!STACK_EMPTY(&context, document.nodes)) {
// node yaml_node_t = POP(&context, document.nodes)
// yaml_free(node.tag)
// switch (node.type) {
// case YAML_SCALAR_NODE:
// yaml_free(node.data.scalar.value)
// break
// case YAML_SEQUENCE_NODE:
// STACK_DEL(&context, node.data.sequence.items)
// break
// case YAML_MAPPING_NODE:
// STACK_DEL(&context, node.data.mapping.pairs)
// break
// default:
// assert(0) // Should not happen.
// }
// }
// STACK_DEL(&context, document.nodes)
//
// yaml_free(document.version_directive)
// for (tag_directive = document.tag_directives.start
// tag_directive != document.tag_directives.end
// tag_directive++) {
// yaml_free(tag_directive.handle)
// yaml_free(tag_directive.prefix)
// }
// yaml_free(document.tag_directives.start)
//
// memset(document, 0, sizeof(yaml_document_t))
//}
//
///**
// * Get a document node.
// */
//
//YAML_DECLARE(yaml_node_t *)
//yaml_document_get_node(document *yaml_document_t, index int)
//{
// assert(document) // Non-NULL document object is expected.
//
// if (index > 0 && document.nodes.start + index <= document.nodes.top) {
// return document.nodes.start + index - 1
// }
// return NULL
//}
//
///**
// * Get the root object.
// */
//
//YAML_DECLARE(yaml_node_t *)
//yaml_document_get_root_node(document *yaml_document_t)
//{
// assert(document) // Non-NULL document object is expected.
//
// if (document.nodes.top != document.nodes.start) {
// return document.nodes.start
// }
// return NULL
//}
//
///*
// * Add a scalar node to a document.
// */
//
//YAML_DECLARE(int)
//yaml_document_add_scalar(document *yaml_document_t,
// tag *yaml_char_t, value *yaml_char_t, length int,
// style yaml_scalar_style_t)
//{
// struct {
// error yaml_error_type_t
// } context
// mark yaml_mark_t = { 0, 0, 0 }
// tag_copy *yaml_char_t = NULL
// value_copy *yaml_char_t = NULL
// node yaml_node_t
//
// assert(document) // Non-NULL document object is expected.
// assert(value) // Non-NULL value is expected.
//
// if (!tag) {
// tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG
// }
//
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
// tag_copy = yaml_strdup(tag)
// if (!tag_copy) goto error
//
// if (length < 0) {
// length = strlen((char *)value)
// }
//
// if (!yaml_check_utf8(value, length)) goto error
// value_copy = yaml_malloc(length+1)
// if (!value_copy) goto error
// memcpy(value_copy, value, length)
// value_copy[length] = '\0'
//
// SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark)
// if (!PUSH(&context, document.nodes, node)) goto error
//
// return document.nodes.top - document.nodes.start
//
//error:
// yaml_free(tag_copy)
// yaml_free(value_copy)
//
// return 0
//}
//
///*
// * Add a sequence node to a document.
// */
//
//YAML_DECLARE(int)
//yaml_document_add_sequence(document *yaml_document_t,
// tag *yaml_char_t, style yaml_sequence_style_t)
//{
// struct {
// error yaml_error_type_t
// } context
// mark yaml_mark_t = { 0, 0, 0 }
// tag_copy *yaml_char_t = NULL
// struct {
// start *yaml_node_item_t
// end *yaml_node_item_t
// top *yaml_node_item_t
// } items = { NULL, NULL, NULL }
// node yaml_node_t
//
// assert(document) // Non-NULL document object is expected.
//
// if (!tag) {
// tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG
// }
//
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
// tag_copy = yaml_strdup(tag)
// if (!tag_copy) goto error
//
// if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error
//
// SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end,
// style, mark, mark)
// if (!PUSH(&context, document.nodes, node)) goto error
//
// return document.nodes.top - document.nodes.start
//
//error:
// STACK_DEL(&context, items)
// yaml_free(tag_copy)
//
// return 0
//}
//
///*
// * Add a mapping node to a document.
// */
//
//YAML_DECLARE(int)
//yaml_document_add_mapping(document *yaml_document_t,
// tag *yaml_char_t, style yaml_mapping_style_t)
//{
// struct {
// error yaml_error_type_t
// } context
// mark yaml_mark_t = { 0, 0, 0 }
// tag_copy *yaml_char_t = NULL
// struct {
// start *yaml_node_pair_t
// end *yaml_node_pair_t
// top *yaml_node_pair_t
// } pairs = { NULL, NULL, NULL }
// node yaml_node_t
//
// assert(document) // Non-NULL document object is expected.
//
// if (!tag) {
// tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG
// }
//
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
// tag_copy = yaml_strdup(tag)
// if (!tag_copy) goto error
//
// if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error
//
// MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end,
// style, mark, mark)
// if (!PUSH(&context, document.nodes, node)) goto error
//
// return document.nodes.top - document.nodes.start
//
//error:
// STACK_DEL(&context, pairs)
// yaml_free(tag_copy)
//
// return 0
//}
//
///*
// * Append an item to a sequence node.
// */
//
//YAML_DECLARE(int)
//yaml_document_append_sequence_item(document *yaml_document_t,
// sequence int, item int)
//{
// struct {
// error yaml_error_type_t
// } context
//
// assert(document) // Non-NULL document is required.
// assert(sequence > 0
// && document.nodes.start + sequence <= document.nodes.top)
// // Valid sequence id is required.
// assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE)
// // A sequence node is required.
// assert(item > 0 && document.nodes.start + item <= document.nodes.top)
// // Valid item id is required.
//
// if (!PUSH(&context,
// document.nodes.start[sequence-1].data.sequence.items, item))
// return 0
//
// return 1
//}
//
///*
// * Append a pair of a key and a value to a mapping node.
// */
//
//YAML_DECLARE(int)
//yaml_document_append_mapping_pair(document *yaml_document_t,
// mapping int, key int, value int)
//{
// struct {
// error yaml_error_type_t
// } context
//
// pair yaml_node_pair_t
//
// assert(document) // Non-NULL document is required.
// assert(mapping > 0
// && document.nodes.start + mapping <= document.nodes.top)
// // Valid mapping id is required.
// assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE)
// // A mapping node is required.
// assert(key > 0 && document.nodes.start + key <= document.nodes.top)
// // Valid key id is required.
// assert(value > 0 && document.nodes.start + value <= document.nodes.top)
// // Valid value id is required.
//
// pair.key = key
// pair.value = value
//
// if (!PUSH(&context,
// document.nodes.start[mapping-1].data.mapping.pairs, pair))
// return 0
//
// return 1
//}
//
//

683
Godeps/_workspace/src/gopkg.in/yaml.v2/decode.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,683 @@
package yaml
import (
"encoding"
"encoding/base64"
"fmt"
"math"
"reflect"
"strconv"
"time"
)
const (
documentNode = 1 << iota
mappingNode
sequenceNode
scalarNode
aliasNode
)
type node struct {
kind int
line, column int
tag string
value string
implicit bool
children []*node
anchors map[string]*node
}
// ----------------------------------------------------------------------------
// Parser, produces a node tree out of a libyaml event stream.
type parser struct {
parser yaml_parser_t
event yaml_event_t
doc *node
}
func newParser(b []byte) *parser {
p := parser{}
if !yaml_parser_initialize(&p.parser) {
panic("failed to initialize YAML emitter")
}
if len(b) == 0 {
b = []byte{'\n'}
}
yaml_parser_set_input_string(&p.parser, b)
p.skip()
if p.event.typ != yaml_STREAM_START_EVENT {
panic("expected stream start event, got " + strconv.Itoa(int(p.event.typ)))
}
p.skip()
return &p
}
func (p *parser) destroy() {
if p.event.typ != yaml_NO_EVENT {
yaml_event_delete(&p.event)
}
yaml_parser_delete(&p.parser)
}
func (p *parser) skip() {
if p.event.typ != yaml_NO_EVENT {
if p.event.typ == yaml_STREAM_END_EVENT {
failf("attempted to go past the end of stream; corrupted value?")
}
yaml_event_delete(&p.event)
}
if !yaml_parser_parse(&p.parser, &p.event) {
p.fail()
}
}
func (p *parser) fail() {
var where string
var line int
if p.parser.problem_mark.line != 0 {
line = p.parser.problem_mark.line
} else if p.parser.context_mark.line != 0 {
line = p.parser.context_mark.line
}
if line != 0 {
where = "line " + strconv.Itoa(line) + ": "
}
var msg string
if len(p.parser.problem) > 0 {
msg = p.parser.problem
} else {
msg = "unknown problem parsing YAML content"
}
failf("%s%s", where, msg)
}
func (p *parser) anchor(n *node, anchor []byte) {
if anchor != nil {
p.doc.anchors[string(anchor)] = n
}
}
func (p *parser) parse() *node {
switch p.event.typ {
case yaml_SCALAR_EVENT:
return p.scalar()
case yaml_ALIAS_EVENT:
return p.alias()
case yaml_MAPPING_START_EVENT:
return p.mapping()
case yaml_SEQUENCE_START_EVENT:
return p.sequence()
case yaml_DOCUMENT_START_EVENT:
return p.document()
case yaml_STREAM_END_EVENT:
// Happens when attempting to decode an empty buffer.
return nil
default:
panic("attempted to parse unknown event: " + strconv.Itoa(int(p.event.typ)))
}
panic("unreachable")
}
func (p *parser) node(kind int) *node {
return &node{
kind: kind,
line: p.event.start_mark.line,
column: p.event.start_mark.column,
}
}
func (p *parser) document() *node {
n := p.node(documentNode)
n.anchors = make(map[string]*node)
p.doc = n
p.skip()
n.children = append(n.children, p.parse())
if p.event.typ != yaml_DOCUMENT_END_EVENT {
panic("expected end of document event but got " + strconv.Itoa(int(p.event.typ)))
}
p.skip()
return n
}
func (p *parser) alias() *node {
n := p.node(aliasNode)
n.value = string(p.event.anchor)
p.skip()
return n
}
func (p *parser) scalar() *node {
n := p.node(scalarNode)
n.value = string(p.event.value)
n.tag = string(p.event.tag)
n.implicit = p.event.implicit
p.anchor(n, p.event.anchor)
p.skip()
return n
}
func (p *parser) sequence() *node {
n := p.node(sequenceNode)
p.anchor(n, p.event.anchor)
p.skip()
for p.event.typ != yaml_SEQUENCE_END_EVENT {
n.children = append(n.children, p.parse())
}
p.skip()
return n
}
func (p *parser) mapping() *node {
n := p.node(mappingNode)
p.anchor(n, p.event.anchor)
p.skip()
for p.event.typ != yaml_MAPPING_END_EVENT {
n.children = append(n.children, p.parse(), p.parse())
}
p.skip()
return n
}
// ----------------------------------------------------------------------------
// Decoder, unmarshals a node into a provided value.
type decoder struct {
doc *node
aliases map[string]bool
mapType reflect.Type
terrors []string
}
var (
mapItemType = reflect.TypeOf(MapItem{})
durationType = reflect.TypeOf(time.Duration(0))
defaultMapType = reflect.TypeOf(map[interface{}]interface{}{})
ifaceType = defaultMapType.Elem()
)
func newDecoder() *decoder {
d := &decoder{mapType: defaultMapType}
d.aliases = make(map[string]bool)
return d
}
func (d *decoder) terror(n *node, tag string, out reflect.Value) {
if n.tag != "" {
tag = n.tag
}
value := n.value
if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG {
if len(value) > 10 {
value = " `" + value[:7] + "...`"
} else {
value = " `" + value + "`"
}
}
d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.line+1, shortTag(tag), value, out.Type()))
}
func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) {
terrlen := len(d.terrors)
err := u.UnmarshalYAML(func(v interface{}) (err error) {
defer handleErr(&err)
d.unmarshal(n, reflect.ValueOf(v))
if len(d.terrors) > terrlen {
issues := d.terrors[terrlen:]
d.terrors = d.terrors[:terrlen]
return &TypeError{issues}
}
return nil
})
if e, ok := err.(*TypeError); ok {
d.terrors = append(d.terrors, e.Errors...)
return false
}
if err != nil {
fail(err)
}
return true
}
// d.prepare initializes and dereferences pointers and calls UnmarshalYAML
// if a value is found to implement it.
// It returns the initialized and dereferenced out value, whether
// unmarshalling was already done by UnmarshalYAML, and if so whether
// its types unmarshalled appropriately.
//
// If n holds a null value, prepare returns before doing anything.
func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) {
if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "") {
return out, false, false
}
again := true
for again {
again = false
if out.Kind() == reflect.Ptr {
if out.IsNil() {
out.Set(reflect.New(out.Type().Elem()))
}
out = out.Elem()
again = true
}
if out.CanAddr() {
if u, ok := out.Addr().Interface().(Unmarshaler); ok {
good = d.callUnmarshaler(n, u)
return out, true, good
}
}
}
return out, false, false
}
func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) {
switch n.kind {
case documentNode:
return d.document(n, out)
case aliasNode:
return d.alias(n, out)
}
out, unmarshaled, good := d.prepare(n, out)
if unmarshaled {
return good
}
switch n.kind {
case scalarNode:
good = d.scalar(n, out)
case mappingNode:
good = d.mapping(n, out)
case sequenceNode:
good = d.sequence(n, out)
default:
panic("internal error: unknown node kind: " + strconv.Itoa(n.kind))
}
return good
}
func (d *decoder) document(n *node, out reflect.Value) (good bool) {
if len(n.children) == 1 {
d.doc = n
d.unmarshal(n.children[0], out)
return true
}
return false
}
func (d *decoder) alias(n *node, out reflect.Value) (good bool) {
an, ok := d.doc.anchors[n.value]
if !ok {
failf("unknown anchor '%s' referenced", n.value)
}
if d.aliases[n.value] {
failf("anchor '%s' value contains itself", n.value)
}
d.aliases[n.value] = true
good = d.unmarshal(an, out)
delete(d.aliases, n.value)
return good
}
var zeroValue reflect.Value
func resetMap(out reflect.Value) {
for _, k := range out.MapKeys() {
out.SetMapIndex(k, zeroValue)
}
}
func (d *decoder) scalar(n *node, out reflect.Value) (good bool) {
var tag string
var resolved interface{}
if n.tag == "" && !n.implicit {
tag = yaml_STR_TAG
resolved = n.value
} else {
tag, resolved = resolve(n.tag, n.value)
if tag == yaml_BINARY_TAG {
data, err := base64.StdEncoding.DecodeString(resolved.(string))
if err != nil {
failf("!!binary value contains invalid base64 data")
}
resolved = string(data)
}
}
if resolved == nil {
if out.Kind() == reflect.Map && !out.CanAddr() {
resetMap(out)
} else {
out.Set(reflect.Zero(out.Type()))
}
return true
}
if s, ok := resolved.(string); ok && out.CanAddr() {
if u, ok := out.Addr().Interface().(encoding.TextUnmarshaler); ok {
err := u.UnmarshalText([]byte(s))
if err != nil {
fail(err)
}
return true
}
}
switch out.Kind() {
case reflect.String:
if tag == yaml_BINARY_TAG {
out.SetString(resolved.(string))
good = true
} else if resolved != nil {
out.SetString(n.value)
good = true
}
case reflect.Interface:
if resolved == nil {
out.Set(reflect.Zero(out.Type()))
} else {
out.Set(reflect.ValueOf(resolved))
}
good = true
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
switch resolved := resolved.(type) {
case int:
if !out.OverflowInt(int64(resolved)) {
out.SetInt(int64(resolved))
good = true
}
case int64:
if !out.OverflowInt(resolved) {
out.SetInt(resolved)
good = true
}
case uint64:
if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
out.SetInt(int64(resolved))
good = true
}
case float64:
if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
out.SetInt(int64(resolved))
good = true
}
case string:
if out.Type() == durationType {
d, err := time.ParseDuration(resolved)
if err == nil {
out.SetInt(int64(d))
good = true
}
}
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
switch resolved := resolved.(type) {
case int:
if resolved >= 0 && !out.OverflowUint(uint64(resolved)) {
out.SetUint(uint64(resolved))
good = true
}
case int64:
if resolved >= 0 && !out.OverflowUint(uint64(resolved)) {
out.SetUint(uint64(resolved))
good = true
}
case uint64:
if !out.OverflowUint(uint64(resolved)) {
out.SetUint(uint64(resolved))
good = true
}
case float64:
if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) {
out.SetUint(uint64(resolved))
good = true
}
}
case reflect.Bool:
switch resolved := resolved.(type) {
case bool:
out.SetBool(resolved)
good = true
}
case reflect.Float32, reflect.Float64:
switch resolved := resolved.(type) {
case int:
out.SetFloat(float64(resolved))
good = true
case int64:
out.SetFloat(float64(resolved))
good = true
case uint64:
out.SetFloat(float64(resolved))
good = true
case float64:
out.SetFloat(resolved)
good = true
}
case reflect.Ptr:
if out.Type().Elem() == reflect.TypeOf(resolved) {
// TODO DOes this make sense? When is out a Ptr except when decoding a nil value?
elem := reflect.New(out.Type().Elem())
elem.Elem().Set(reflect.ValueOf(resolved))
out.Set(elem)
good = true
}
}
if !good {
d.terror(n, tag, out)
}
return good
}
func settableValueOf(i interface{}) reflect.Value {
v := reflect.ValueOf(i)
sv := reflect.New(v.Type()).Elem()
sv.Set(v)
return sv
}
func (d *decoder) sequence(n *node, out reflect.Value) (good bool) {
l := len(n.children)
var iface reflect.Value
switch out.Kind() {
case reflect.Slice:
out.Set(reflect.MakeSlice(out.Type(), l, l))
case reflect.Interface:
// No type hints. Will have to use a generic sequence.
iface = out
out = settableValueOf(make([]interface{}, l))
default:
d.terror(n, yaml_SEQ_TAG, out)
return false
}
et := out.Type().Elem()
j := 0
for i := 0; i < l; i++ {
e := reflect.New(et).Elem()
if ok := d.unmarshal(n.children[i], e); ok {
out.Index(j).Set(e)
j++
}
}
out.Set(out.Slice(0, j))
if iface.IsValid() {
iface.Set(out)
}
return true
}
func (d *decoder) mapping(n *node, out reflect.Value) (good bool) {
switch out.Kind() {
case reflect.Struct:
return d.mappingStruct(n, out)
case reflect.Slice:
return d.mappingSlice(n, out)
case reflect.Map:
// okay
case reflect.Interface:
if d.mapType.Kind() == reflect.Map {
iface := out
out = reflect.MakeMap(d.mapType)
iface.Set(out)
} else {
slicev := reflect.New(d.mapType).Elem()
if !d.mappingSlice(n, slicev) {
return false
}
out.Set(slicev)
return true
}
default:
d.terror(n, yaml_MAP_TAG, out)
return false
}
outt := out.Type()
kt := outt.Key()
et := outt.Elem()
mapType := d.mapType
if outt.Key() == ifaceType && outt.Elem() == ifaceType {
d.mapType = outt
}
if out.IsNil() {
out.Set(reflect.MakeMap(outt))
}
l := len(n.children)
for i := 0; i < l; i += 2 {
if isMerge(n.children[i]) {
d.merge(n.children[i+1], out)
continue
}
k := reflect.New(kt).Elem()
if d.unmarshal(n.children[i], k) {
kkind := k.Kind()
if kkind == reflect.Interface {
kkind = k.Elem().Kind()
}
if kkind == reflect.Map || kkind == reflect.Slice {
failf("invalid map key: %#v", k.Interface())
}
e := reflect.New(et).Elem()
if d.unmarshal(n.children[i+1], e) {
out.SetMapIndex(k, e)
}
}
}
d.mapType = mapType
return true
}
func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) {
outt := out.Type()
if outt.Elem() != mapItemType {
d.terror(n, yaml_MAP_TAG, out)
return false
}
mapType := d.mapType
d.mapType = outt
var slice []MapItem
var l = len(n.children)
for i := 0; i < l; i += 2 {
if isMerge(n.children[i]) {
d.merge(n.children[i+1], out)
continue
}
item := MapItem{}
k := reflect.ValueOf(&item.Key).Elem()
if d.unmarshal(n.children[i], k) {
v := reflect.ValueOf(&item.Value).Elem()
if d.unmarshal(n.children[i+1], v) {
slice = append(slice, item)
}
}
}
out.Set(reflect.ValueOf(slice))
d.mapType = mapType
return true
}
func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
sinfo, err := getStructInfo(out.Type())
if err != nil {
panic(err)
}
name := settableValueOf("")
l := len(n.children)
var inlineMap reflect.Value
var elemType reflect.Type
if sinfo.InlineMap != -1 {
inlineMap = out.Field(sinfo.InlineMap)
inlineMap.Set(reflect.New(inlineMap.Type()).Elem())
elemType = inlineMap.Type().Elem()
}
for i := 0; i < l; i += 2 {
ni := n.children[i]
if isMerge(ni) {
d.merge(n.children[i+1], out)
continue
}
if !d.unmarshal(ni, name) {
continue
}
if info, ok := sinfo.FieldsMap[name.String()]; ok {
var field reflect.Value
if info.Inline == nil {
field = out.Field(info.Num)
} else {
field = out.FieldByIndex(info.Inline)
}
d.unmarshal(n.children[i+1], field)
} else if sinfo.InlineMap != -1 {
if inlineMap.IsNil() {
inlineMap.Set(reflect.MakeMap(inlineMap.Type()))
}
value := reflect.New(elemType).Elem()
d.unmarshal(n.children[i+1], value)
inlineMap.SetMapIndex(name, value)
}
}
return true
}
func failWantMap() {
failf("map merge requires map or sequence of maps as the value")
}
func (d *decoder) merge(n *node, out reflect.Value) {
switch n.kind {
case mappingNode:
d.unmarshal(n, out)
case aliasNode:
an, ok := d.doc.anchors[n.value]
if ok && an.kind != mappingNode {
failWantMap()
}
d.unmarshal(n, out)
case sequenceNode:
// Step backwards as earlier nodes take precedence.
for i := len(n.children) - 1; i >= 0; i-- {
ni := n.children[i]
if ni.kind == aliasNode {
an, ok := d.doc.anchors[ni.value]
if ok && an.kind != mappingNode {
failWantMap()
}
} else if ni.kind != mappingNode {
failWantMap()
}
d.unmarshal(ni, out)
}
default:
failWantMap()
}
}
func isMerge(n *node) bool {
return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG)
}

1685
Godeps/_workspace/src/gopkg.in/yaml.v2/emitterc.go сгенерированный поставляемый Обычный файл

Разница между файлами не показана из-за своего большого размера Загрузить разницу

306
Godeps/_workspace/src/gopkg.in/yaml.v2/encode.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,306 @@
package yaml
import (
"encoding"
"fmt"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
"time"
)
type encoder struct {
emitter yaml_emitter_t
event yaml_event_t
out []byte
flow bool
}
func newEncoder() (e *encoder) {
e = &encoder{}
e.must(yaml_emitter_initialize(&e.emitter))
yaml_emitter_set_output_string(&e.emitter, &e.out)
yaml_emitter_set_unicode(&e.emitter, true)
e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING))
e.emit()
e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true))
e.emit()
return e
}
func (e *encoder) finish() {
e.must(yaml_document_end_event_initialize(&e.event, true))
e.emit()
e.emitter.open_ended = false
e.must(yaml_stream_end_event_initialize(&e.event))
e.emit()
}
func (e *encoder) destroy() {
yaml_emitter_delete(&e.emitter)
}
func (e *encoder) emit() {
// This will internally delete the e.event value.
if !yaml_emitter_emit(&e.emitter, &e.event) && e.event.typ != yaml_DOCUMENT_END_EVENT && e.event.typ != yaml_STREAM_END_EVENT {
e.must(false)
}
}
func (e *encoder) must(ok bool) {
if !ok {
msg := e.emitter.problem
if msg == "" {
msg = "unknown problem generating YAML content"
}
failf("%s", msg)
}
}
func (e *encoder) marshal(tag string, in reflect.Value) {
if !in.IsValid() {
e.nilv()
return
}
iface := in.Interface()
if m, ok := iface.(Marshaler); ok {
v, err := m.MarshalYAML()
if err != nil {
fail(err)
}
if v == nil {
e.nilv()
return
}
in = reflect.ValueOf(v)
} else if m, ok := iface.(encoding.TextMarshaler); ok {
text, err := m.MarshalText()
if err != nil {
fail(err)
}
in = reflect.ValueOf(string(text))
}
switch in.Kind() {
case reflect.Interface:
if in.IsNil() {
e.nilv()
} else {
e.marshal(tag, in.Elem())
}
case reflect.Map:
e.mapv(tag, in)
case reflect.Ptr:
if in.IsNil() {
e.nilv()
} else {
e.marshal(tag, in.Elem())
}
case reflect.Struct:
e.structv(tag, in)
case reflect.Slice:
if in.Type().Elem() == mapItemType {
e.itemsv(tag, in)
} else {
e.slicev(tag, in)
}
case reflect.String:
e.stringv(tag, in)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if in.Type() == durationType {
e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String()))
} else {
e.intv(tag, in)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
e.uintv(tag, in)
case reflect.Float32, reflect.Float64:
e.floatv(tag, in)
case reflect.Bool:
e.boolv(tag, in)
default:
panic("cannot marshal type: " + in.Type().String())
}
}
func (e *encoder) mapv(tag string, in reflect.Value) {
e.mappingv(tag, func() {
keys := keyList(in.MapKeys())
sort.Sort(keys)
for _, k := range keys {
e.marshal("", k)
e.marshal("", in.MapIndex(k))
}
})
}
func (e *encoder) itemsv(tag string, in reflect.Value) {
e.mappingv(tag, func() {
slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem)
for _, item := range slice {
e.marshal("", reflect.ValueOf(item.Key))
e.marshal("", reflect.ValueOf(item.Value))
}
})
}
func (e *encoder) structv(tag string, in reflect.Value) {
sinfo, err := getStructInfo(in.Type())
if err != nil {
panic(err)
}
e.mappingv(tag, func() {
for _, info := range sinfo.FieldsList {
var value reflect.Value
if info.Inline == nil {
value = in.Field(info.Num)
} else {
value = in.FieldByIndex(info.Inline)
}
if info.OmitEmpty && isZero(value) {
continue
}
e.marshal("", reflect.ValueOf(info.Key))
e.flow = info.Flow
e.marshal("", value)
}
if sinfo.InlineMap >= 0 {
m := in.Field(sinfo.InlineMap)
if m.Len() > 0 {
e.flow = false
keys := keyList(m.MapKeys())
sort.Sort(keys)
for _, k := range keys {
if _, found := sinfo.FieldsMap[k.String()]; found {
panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String()))
}
e.marshal("", k)
e.flow = false
e.marshal("", m.MapIndex(k))
}
}
}
})
}
func (e *encoder) mappingv(tag string, f func()) {
implicit := tag == ""
style := yaml_BLOCK_MAPPING_STYLE
if e.flow {
e.flow = false
style = yaml_FLOW_MAPPING_STYLE
}
e.must(yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
e.emit()
f()
e.must(yaml_mapping_end_event_initialize(&e.event))
e.emit()
}
func (e *encoder) slicev(tag string, in reflect.Value) {
implicit := tag == ""
style := yaml_BLOCK_SEQUENCE_STYLE
if e.flow {
e.flow = false
style = yaml_FLOW_SEQUENCE_STYLE
}
e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
e.emit()
n := in.Len()
for i := 0; i < n; i++ {
e.marshal("", in.Index(i))
}
e.must(yaml_sequence_end_event_initialize(&e.event))
e.emit()
}
// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1.
//
// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported
// in YAML 1.2 and by this package, but these should be marshalled quoted for
// the time being for compatibility with other parsers.
func isBase60Float(s string) (result bool) {
// Fast path.
if s == "" {
return false
}
c := s[0]
if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 {
return false
}
// Do the full match.
return base60float.MatchString(s)
}
// From http://yaml.org/type/float.html, except the regular expression there
// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
func (e *encoder) stringv(tag string, in reflect.Value) {
var style yaml_scalar_style_t
s := in.String()
rtag, rs := resolve("", s)
if rtag == yaml_BINARY_TAG {
if tag == "" || tag == yaml_STR_TAG {
tag = rtag
s = rs.(string)
} else if tag == yaml_BINARY_TAG {
failf("explicitly tagged !!binary data must be base64-encoded")
} else {
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
}
}
if tag == "" && (rtag != yaml_STR_TAG || isBase60Float(s)) {
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
} else if strings.Contains(s, "\n") {
style = yaml_LITERAL_SCALAR_STYLE
} else {
style = yaml_PLAIN_SCALAR_STYLE
}
e.emitScalar(s, "", tag, style)
}
func (e *encoder) boolv(tag string, in reflect.Value) {
var s string
if in.Bool() {
s = "true"
} else {
s = "false"
}
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) intv(tag string, in reflect.Value) {
s := strconv.FormatInt(in.Int(), 10)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) uintv(tag string, in reflect.Value) {
s := strconv.FormatUint(in.Uint(), 10)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) floatv(tag string, in reflect.Value) {
// FIXME: Handle 64 bits here.
s := strconv.FormatFloat(float64(in.Float()), 'g', -1, 32)
switch s {
case "+Inf":
s = ".inf"
case "-Inf":
s = "-.inf"
case "NaN":
s = ".nan"
}
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) nilv() {
e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) {
implicit := tag == ""
e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
e.emit()
}

1096
Godeps/_workspace/src/gopkg.in/yaml.v2/parserc.go сгенерированный поставляемый Обычный файл

Разница между файлами не показана из-за своего большого размера Загрузить разницу

394
Godeps/_workspace/src/gopkg.in/yaml.v2/readerc.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,394 @@
package yaml
import (
"io"
)
// Set the reader error and return 0.
func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool {
parser.error = yaml_READER_ERROR
parser.problem = problem
parser.problem_offset = offset
parser.problem_value = value
return false
}
// Byte order marks.
const (
bom_UTF8 = "\xef\xbb\xbf"
bom_UTF16LE = "\xff\xfe"
bom_UTF16BE = "\xfe\xff"
)
// Determine the input stream encoding by checking the BOM symbol. If no BOM is
// found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure.
func yaml_parser_determine_encoding(parser *yaml_parser_t) bool {
// Ensure that we had enough bytes in the raw buffer.
for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 {
if !yaml_parser_update_raw_buffer(parser) {
return false
}
}
// Determine the encoding.
buf := parser.raw_buffer
pos := parser.raw_buffer_pos
avail := len(buf) - pos
if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] {
parser.encoding = yaml_UTF16LE_ENCODING
parser.raw_buffer_pos += 2
parser.offset += 2
} else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] {
parser.encoding = yaml_UTF16BE_ENCODING
parser.raw_buffer_pos += 2
parser.offset += 2
} else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] {
parser.encoding = yaml_UTF8_ENCODING
parser.raw_buffer_pos += 3
parser.offset += 3
} else {
parser.encoding = yaml_UTF8_ENCODING
}
return true
}
// Update the raw buffer.
func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool {
size_read := 0
// Return if the raw buffer is full.
if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) {
return true
}
// Return on EOF.
if parser.eof {
return true
}
// Move the remaining bytes in the raw buffer to the beginning.
if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) {
copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:])
}
parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos]
parser.raw_buffer_pos = 0
// Call the read handler to fill the buffer.
size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)])
parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read]
if err == io.EOF {
parser.eof = true
} else if err != nil {
return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1)
}
return true
}
// Ensure that the buffer contains at least `length` characters.
// Return true on success, false on failure.
//
// The length is supposed to be significantly less that the buffer size.
func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool {
if parser.read_handler == nil {
panic("read handler must be set")
}
// If the EOF flag is set and the raw buffer is empty, do nothing.
if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) {
return true
}
// Return if the buffer contains enough characters.
if parser.unread >= length {
return true
}
// Determine the input encoding if it is not known yet.
if parser.encoding == yaml_ANY_ENCODING {
if !yaml_parser_determine_encoding(parser) {
return false
}
}
// Move the unread characters to the beginning of the buffer.
buffer_len := len(parser.buffer)
if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len {
copy(parser.buffer, parser.buffer[parser.buffer_pos:])
buffer_len -= parser.buffer_pos
parser.buffer_pos = 0
} else if parser.buffer_pos == buffer_len {
buffer_len = 0
parser.buffer_pos = 0
}
// Open the whole buffer for writing, and cut it before returning.
parser.buffer = parser.buffer[:cap(parser.buffer)]
// Fill the buffer until it has enough characters.
first := true
for parser.unread < length {
// Fill the raw buffer if necessary.
if !first || parser.raw_buffer_pos == len(parser.raw_buffer) {
if !yaml_parser_update_raw_buffer(parser) {
parser.buffer = parser.buffer[:buffer_len]
return false
}
}
first = false
// Decode the raw buffer.
inner:
for parser.raw_buffer_pos != len(parser.raw_buffer) {
var value rune
var width int
raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos
// Decode the next character.
switch parser.encoding {
case yaml_UTF8_ENCODING:
// Decode a UTF-8 character. Check RFC 3629
// (http://www.ietf.org/rfc/rfc3629.txt) for more details.
//
// The following table (taken from the RFC) is used for
// decoding.
//
// Char. number range | UTF-8 octet sequence
// (hexadecimal) | (binary)
// --------------------+------------------------------------
// 0000 0000-0000 007F | 0xxxxxxx
// 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
// 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
// 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
//
// Additionally, the characters in the range 0xD800-0xDFFF
// are prohibited as they are reserved for use with UTF-16
// surrogate pairs.
// Determine the length of the UTF-8 sequence.
octet := parser.raw_buffer[parser.raw_buffer_pos]
switch {
case octet&0x80 == 0x00:
width = 1
case octet&0xE0 == 0xC0:
width = 2
case octet&0xF0 == 0xE0:
width = 3
case octet&0xF8 == 0xF0:
width = 4
default:
// The leading octet is invalid.
return yaml_parser_set_reader_error(parser,
"invalid leading UTF-8 octet",
parser.offset, int(octet))
}
// Check if the raw buffer contains an incomplete character.
if width > raw_unread {
if parser.eof {
return yaml_parser_set_reader_error(parser,
"incomplete UTF-8 octet sequence",
parser.offset, -1)
}
break inner
}
// Decode the leading octet.
switch {
case octet&0x80 == 0x00:
value = rune(octet & 0x7F)
case octet&0xE0 == 0xC0:
value = rune(octet & 0x1F)
case octet&0xF0 == 0xE0:
value = rune(octet & 0x0F)
case octet&0xF8 == 0xF0:
value = rune(octet & 0x07)
default:
value = 0
}
// Check and decode the trailing octets.
for k := 1; k < width; k++ {
octet = parser.raw_buffer[parser.raw_buffer_pos+k]
// Check if the octet is valid.
if (octet & 0xC0) != 0x80 {
return yaml_parser_set_reader_error(parser,
"invalid trailing UTF-8 octet",
parser.offset+k, int(octet))
}
// Decode the octet.
value = (value << 6) + rune(octet&0x3F)
}
// Check the length of the sequence against the value.
switch {
case width == 1:
case width == 2 && value >= 0x80:
case width == 3 && value >= 0x800:
case width == 4 && value >= 0x10000:
default:
return yaml_parser_set_reader_error(parser,
"invalid length of a UTF-8 sequence",
parser.offset, -1)
}
// Check the range of the value.
if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF {
return yaml_parser_set_reader_error(parser,
"invalid Unicode character",
parser.offset, int(value))
}
case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING:
var low, high int
if parser.encoding == yaml_UTF16LE_ENCODING {
low, high = 0, 1
} else {
low, high = 1, 0
}
// The UTF-16 encoding is not as simple as one might
// naively think. Check RFC 2781
// (http://www.ietf.org/rfc/rfc2781.txt).
//
// Normally, two subsequent bytes describe a Unicode
// character. However a special technique (called a
// surrogate pair) is used for specifying character
// values larger than 0xFFFF.
//
// A surrogate pair consists of two pseudo-characters:
// high surrogate area (0xD800-0xDBFF)
// low surrogate area (0xDC00-0xDFFF)
//
// The following formulas are used for decoding
// and encoding characters using surrogate pairs:
//
// U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF)
// U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF)
// W1 = 110110yyyyyyyyyy
// W2 = 110111xxxxxxxxxx
//
// where U is the character value, W1 is the high surrogate
// area, W2 is the low surrogate area.
// Check for incomplete UTF-16 character.
if raw_unread < 2 {
if parser.eof {
return yaml_parser_set_reader_error(parser,
"incomplete UTF-16 character",
parser.offset, -1)
}
break inner
}
// Get the character.
value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) +
(rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8)
// Check for unexpected low surrogate area.
if value&0xFC00 == 0xDC00 {
return yaml_parser_set_reader_error(parser,
"unexpected low surrogate area",
parser.offset, int(value))
}
// Check for a high surrogate area.
if value&0xFC00 == 0xD800 {
width = 4
// Check for incomplete surrogate pair.
if raw_unread < 4 {
if parser.eof {
return yaml_parser_set_reader_error(parser,
"incomplete UTF-16 surrogate pair",
parser.offset, -1)
}
break inner
}
// Get the next character.
value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) +
(rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8)
// Check for a low surrogate area.
if value2&0xFC00 != 0xDC00 {
return yaml_parser_set_reader_error(parser,
"expected low surrogate area",
parser.offset+2, int(value2))
}
// Generate the value of the surrogate pair.
value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF)
} else {
width = 2
}
default:
panic("impossible")
}
// Check if the character is in the allowed range:
// #x9 | #xA | #xD | [#x20-#x7E] (8 bit)
// | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit)
// | [#x10000-#x10FFFF] (32 bit)
switch {
case value == 0x09:
case value == 0x0A:
case value == 0x0D:
case value >= 0x20 && value <= 0x7E:
case value == 0x85:
case value >= 0xA0 && value <= 0xD7FF:
case value >= 0xE000 && value <= 0xFFFD:
case value >= 0x10000 && value <= 0x10FFFF:
default:
return yaml_parser_set_reader_error(parser,
"control characters are not allowed",
parser.offset, int(value))
}
// Move the raw pointers.
parser.raw_buffer_pos += width
parser.offset += width
// Finally put the character into the buffer.
if value <= 0x7F {
// 0000 0000-0000 007F . 0xxxxxxx
parser.buffer[buffer_len+0] = byte(value)
buffer_len += 1
} else if value <= 0x7FF {
// 0000 0080-0000 07FF . 110xxxxx 10xxxxxx
parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6))
parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F))
buffer_len += 2
} else if value <= 0xFFFF {
// 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx
parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12))
parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F))
parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F))
buffer_len += 3
} else {
// 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18))
parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F))
parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F))
parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F))
buffer_len += 4
}
parser.unread++
}
// On EOF, put NUL into the buffer and return.
if parser.eof {
parser.buffer[buffer_len] = 0
buffer_len++
parser.unread++
break
}
}
parser.buffer = parser.buffer[:buffer_len]
return true
}

203
Godeps/_workspace/src/gopkg.in/yaml.v2/resolve.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,203 @@
package yaml
import (
"encoding/base64"
"math"
"strconv"
"strings"
"unicode/utf8"
)
type resolveMapItem struct {
value interface{}
tag string
}
var resolveTable = make([]byte, 256)
var resolveMap = make(map[string]resolveMapItem)
func init() {
t := resolveTable
t[int('+')] = 'S' // Sign
t[int('-')] = 'S'
for _, c := range "0123456789" {
t[int(c)] = 'D' // Digit
}
for _, c := range "yYnNtTfFoO~" {
t[int(c)] = 'M' // In map
}
t[int('.')] = '.' // Float (potentially in map)
var resolveMapList = []struct {
v interface{}
tag string
l []string
}{
{true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}},
{true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}},
{true, yaml_BOOL_TAG, []string{"on", "On", "ON"}},
{false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}},
{false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}},
{false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}},
{nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}},
{math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}},
{math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}},
{math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}},
{math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}},
{"<<", yaml_MERGE_TAG, []string{"<<"}},
}
m := resolveMap
for _, item := range resolveMapList {
for _, s := range item.l {
m[s] = resolveMapItem{item.v, item.tag}
}
}
}
const longTagPrefix = "tag:yaml.org,2002:"
func shortTag(tag string) string {
// TODO This can easily be made faster and produce less garbage.
if strings.HasPrefix(tag, longTagPrefix) {
return "!!" + tag[len(longTagPrefix):]
}
return tag
}
func longTag(tag string) string {
if strings.HasPrefix(tag, "!!") {
return longTagPrefix + tag[2:]
}
return tag
}
func resolvableTag(tag string) bool {
switch tag {
case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG:
return true
}
return false
}
func resolve(tag string, in string) (rtag string, out interface{}) {
if !resolvableTag(tag) {
return tag, in
}
defer func() {
switch tag {
case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG:
return
}
failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag))
}()
// Any data is accepted as a !!str or !!binary.
// Otherwise, the prefix is enough of a hint about what it might be.
hint := byte('N')
if in != "" {
hint = resolveTable[in[0]]
}
if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG {
// Handle things we can lookup in a map.
if item, ok := resolveMap[in]; ok {
return item.tag, item.value
}
// Base 60 floats are a bad idea, were dropped in YAML 1.2, and
// are purposefully unsupported here. They're still quoted on
// the way out for compatibility with other parser, though.
switch hint {
case 'M':
// We've already checked the map above.
case '.':
// Not in the map, so maybe a normal float.
floatv, err := strconv.ParseFloat(in, 64)
if err == nil {
return yaml_FLOAT_TAG, floatv
}
case 'D', 'S':
// Int, float, or timestamp.
plain := strings.Replace(in, "_", "", -1)
intv, err := strconv.ParseInt(plain, 0, 64)
if err == nil {
if intv == int64(int(intv)) {
return yaml_INT_TAG, int(intv)
} else {
return yaml_INT_TAG, intv
}
}
uintv, err := strconv.ParseUint(plain, 0, 64)
if err == nil {
return yaml_INT_TAG, uintv
}
floatv, err := strconv.ParseFloat(plain, 64)
if err == nil {
return yaml_FLOAT_TAG, floatv
}
if strings.HasPrefix(plain, "0b") {
intv, err := strconv.ParseInt(plain[2:], 2, 64)
if err == nil {
if intv == int64(int(intv)) {
return yaml_INT_TAG, int(intv)
} else {
return yaml_INT_TAG, intv
}
}
uintv, err := strconv.ParseUint(plain[2:], 2, 64)
if err == nil {
return yaml_INT_TAG, uintv
}
} else if strings.HasPrefix(plain, "-0b") {
intv, err := strconv.ParseInt(plain[3:], 2, 64)
if err == nil {
if intv == int64(int(intv)) {
return yaml_INT_TAG, -int(intv)
} else {
return yaml_INT_TAG, -intv
}
}
}
// XXX Handle timestamps here.
default:
panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")")
}
}
if tag == yaml_BINARY_TAG {
return yaml_BINARY_TAG, in
}
if utf8.ValidString(in) {
return yaml_STR_TAG, in
}
return yaml_BINARY_TAG, encodeBase64(in)
}
// encodeBase64 encodes s as base64 that is broken up into multiple lines
// as appropriate for the resulting length.
func encodeBase64(s string) string {
const lineLen = 70
encLen := base64.StdEncoding.EncodedLen(len(s))
lines := encLen/lineLen + 1
buf := make([]byte, encLen*2+lines)
in := buf[0:encLen]
out := buf[encLen:]
base64.StdEncoding.Encode(in, []byte(s))
k := 0
for i := 0; i < len(in); i += lineLen {
j := i + lineLen
if j > len(in) {
j = len(in)
}
k += copy(out[k:], in[i:j])
if lines > 1 {
out[k] = '\n'
k++
}
}
return string(out[:k])
}

2710
Godeps/_workspace/src/gopkg.in/yaml.v2/scannerc.go сгенерированный поставляемый Обычный файл

Разница между файлами не показана из-за своего большого размера Загрузить разницу

104
Godeps/_workspace/src/gopkg.in/yaml.v2/sorter.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,104 @@
package yaml
import (
"reflect"
"unicode"
)
type keyList []reflect.Value
func (l keyList) Len() int { return len(l) }
func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l keyList) Less(i, j int) bool {
a := l[i]
b := l[j]
ak := a.Kind()
bk := b.Kind()
for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() {
a = a.Elem()
ak = a.Kind()
}
for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() {
b = b.Elem()
bk = b.Kind()
}
af, aok := keyFloat(a)
bf, bok := keyFloat(b)
if aok && bok {
if af != bf {
return af < bf
}
if ak != bk {
return ak < bk
}
return numLess(a, b)
}
if ak != reflect.String || bk != reflect.String {
return ak < bk
}
ar, br := []rune(a.String()), []rune(b.String())
for i := 0; i < len(ar) && i < len(br); i++ {
if ar[i] == br[i] {
continue
}
al := unicode.IsLetter(ar[i])
bl := unicode.IsLetter(br[i])
if al && bl {
return ar[i] < br[i]
}
if al || bl {
return bl
}
var ai, bi int
var an, bn int64
for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ {
an = an*10 + int64(ar[ai]-'0')
}
for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ {
bn = bn*10 + int64(br[bi]-'0')
}
if an != bn {
return an < bn
}
if ai != bi {
return ai < bi
}
return ar[i] < br[i]
}
return len(ar) < len(br)
}
// keyFloat returns a float value for v if it is a number/bool
// and whether it is a number/bool or not.
func keyFloat(v reflect.Value) (f float64, ok bool) {
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float64(v.Int()), true
case reflect.Float32, reflect.Float64:
return v.Float(), true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return float64(v.Uint()), true
case reflect.Bool:
if v.Bool() {
return 1, true
}
return 0, true
}
return 0, false
}
// numLess returns whether a < b.
// a and b must necessarily have the same kind.
func numLess(a, b reflect.Value) bool {
switch a.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return a.Int() < b.Int()
case reflect.Float32, reflect.Float64:
return a.Float() < b.Float()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return a.Uint() < b.Uint()
case reflect.Bool:
return !a.Bool() && b.Bool()
}
panic("not a number")
}

89
Godeps/_workspace/src/gopkg.in/yaml.v2/writerc.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,89 @@
package yaml
// Set the writer error and return false.
func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool {
emitter.error = yaml_WRITER_ERROR
emitter.problem = problem
return false
}
// Flush the output buffer.
func yaml_emitter_flush(emitter *yaml_emitter_t) bool {
if emitter.write_handler == nil {
panic("write handler not set")
}
// Check if the buffer is empty.
if emitter.buffer_pos == 0 {
return true
}
// If the output encoding is UTF-8, we don't need to recode the buffer.
if emitter.encoding == yaml_UTF8_ENCODING {
if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil {
return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
}
emitter.buffer_pos = 0
return true
}
// Recode the buffer into the raw buffer.
var low, high int
if emitter.encoding == yaml_UTF16LE_ENCODING {
low, high = 0, 1
} else {
high, low = 1, 0
}
pos := 0
for pos < emitter.buffer_pos {
// See the "reader.c" code for more details on UTF-8 encoding. Note
// that we assume that the buffer contains a valid UTF-8 sequence.
// Read the next UTF-8 character.
octet := emitter.buffer[pos]
var w int
var value rune
switch {
case octet&0x80 == 0x00:
w, value = 1, rune(octet&0x7F)
case octet&0xE0 == 0xC0:
w, value = 2, rune(octet&0x1F)
case octet&0xF0 == 0xE0:
w, value = 3, rune(octet&0x0F)
case octet&0xF8 == 0xF0:
w, value = 4, rune(octet&0x07)
}
for k := 1; k < w; k++ {
octet = emitter.buffer[pos+k]
value = (value << 6) + (rune(octet) & 0x3F)
}
pos += w
// Write the character.
if value < 0x10000 {
var b [2]byte
b[high] = byte(value >> 8)
b[low] = byte(value & 0xFF)
emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1])
} else {
// Write the character using a surrogate pair (check "reader.c").
var b [4]byte
value -= 0x10000
b[high] = byte(0xD8 + (value >> 18))
b[low] = byte((value >> 10) & 0xFF)
b[high+2] = byte(0xDC + ((value >> 8) & 0xFF))
b[low+2] = byte(value & 0xFF)
emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1], b[2], b[3])
}
}
// Write the raw buffer.
if err := emitter.write_handler(emitter, emitter.raw_buffer); err != nil {
return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
}
emitter.buffer_pos = 0
emitter.raw_buffer = emitter.raw_buffer[:0]
return true
}

346
Godeps/_workspace/src/gopkg.in/yaml.v2/yaml.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,346 @@
// Package yaml implements YAML support for the Go language.
//
// Source code and other details for the project are available at GitHub:
//
// https://github.com/go-yaml/yaml
//
package yaml
import (
"errors"
"fmt"
"reflect"
"strings"
"sync"
)
// MapSlice encodes and decodes as a YAML map.
// The order of keys is preserved when encoding and decoding.
type MapSlice []MapItem
// MapItem is an item in a MapSlice.
type MapItem struct {
Key, Value interface{}
}
// The Unmarshaler interface may be implemented by types to customize their
// behavior when being unmarshaled from a YAML document. The UnmarshalYAML
// method receives a function that may be called to unmarshal the original
// YAML value into a field or variable. It is safe to call the unmarshal
// function parameter more than once if necessary.
type Unmarshaler interface {
UnmarshalYAML(unmarshal func(interface{}) error) error
}
// The Marshaler interface may be implemented by types to customize their
// behavior when being marshaled into a YAML document. The returned value
// is marshaled in place of the original value implementing Marshaler.
//
// If an error is returned by MarshalYAML, the marshaling procedure stops
// and returns with the provided error.
type Marshaler interface {
MarshalYAML() (interface{}, error)
}
// Unmarshal decodes the first document found within the in byte slice
// and assigns decoded values into the out value.
//
// Maps and pointers (to a struct, string, int, etc) are accepted as out
// values. If an internal pointer within a struct is not initialized,
// the yaml package will initialize it if necessary for unmarshalling
// the provided data. The out parameter must not be nil.
//
// The type of the decoded values should be compatible with the respective
// values in out. If one or more values cannot be decoded due to a type
// mismatches, decoding continues partially until the end of the YAML
// content, and a *yaml.TypeError is returned with details for all
// missed values.
//
// Struct fields are only unmarshalled if they are exported (have an
// upper case first letter), and are unmarshalled using the field name
// lowercased as the default key. Custom keys may be defined via the
// "yaml" name in the field tag: the content preceding the first comma
// is used as the key, and the following comma-separated options are
// used to tweak the marshalling process (see Marshal).
// Conflicting names result in a runtime error.
//
// For example:
//
// type T struct {
// F int `yaml:"a,omitempty"`
// B int
// }
// var t T
// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
//
// See the documentation of Marshal for the format of tags and a list of
// supported tag options.
//
func Unmarshal(in []byte, out interface{}) (err error) {
defer handleErr(&err)
d := newDecoder()
p := newParser(in)
defer p.destroy()
node := p.parse()
if node != nil {
v := reflect.ValueOf(out)
if v.Kind() == reflect.Ptr && !v.IsNil() {
v = v.Elem()
}
d.unmarshal(node, v)
}
if len(d.terrors) > 0 {
return &TypeError{d.terrors}
}
return nil
}
// Marshal serializes the value provided into a YAML document. The structure
// of the generated document will reflect the structure of the value itself.
// Maps and pointers (to struct, string, int, etc) are accepted as the in value.
//
// Struct fields are only unmarshalled if they are exported (have an upper case
// first letter), and are unmarshalled using the field name lowercased as the
// default key. Custom keys may be defined via the "yaml" name in the field
// tag: the content preceding the first comma is used as the key, and the
// following comma-separated options are used to tweak the marshalling process.
// Conflicting names result in a runtime error.
//
// The field tag format accepted is:
//
// `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
//
// The following flags are currently supported:
//
// omitempty Only include the field if it's not set to the zero
// value for the type or to empty slices or maps.
// Does not apply to zero valued structs.
//
// flow Marshal using a flow style (useful for structs,
// sequences and maps).
//
// inline Inline the field, which must be a struct or a map,
// causing all of its fields or keys to be processed as if
// they were part of the outer struct. For maps, keys must
// not conflict with the yaml keys of other struct fields.
//
// In addition, if the key is "-", the field is ignored.
//
// For example:
//
// type T struct {
// F int "a,omitempty"
// B int
// }
// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
//
func Marshal(in interface{}) (out []byte, err error) {
defer handleErr(&err)
e := newEncoder()
defer e.destroy()
e.marshal("", reflect.ValueOf(in))
e.finish()
out = e.out
return
}
func handleErr(err *error) {
if v := recover(); v != nil {
if e, ok := v.(yamlError); ok {
*err = e.err
} else {
panic(v)
}
}
}
type yamlError struct {
err error
}
func fail(err error) {
panic(yamlError{err})
}
func failf(format string, args ...interface{}) {
panic(yamlError{fmt.Errorf("yaml: "+format, args...)})
}
// A TypeError is returned by Unmarshal when one or more fields in
// the YAML document cannot be properly decoded into the requested
// types. When this error is returned, the value is still
// unmarshaled partially.
type TypeError struct {
Errors []string
}
func (e *TypeError) Error() string {
return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n "))
}
// --------------------------------------------------------------------------
// Maintain a mapping of keys to structure field indexes
// The code in this section was copied from mgo/bson.
// structInfo holds details for the serialization of fields of
// a given struct.
type structInfo struct {
FieldsMap map[string]fieldInfo
FieldsList []fieldInfo
// InlineMap is the number of the field in the struct that
// contains an ,inline map, or -1 if there's none.
InlineMap int
}
type fieldInfo struct {
Key string
Num int
OmitEmpty bool
Flow bool
// Inline holds the field index if the field is part of an inlined struct.
Inline []int
}
var structMap = make(map[reflect.Type]*structInfo)
var fieldMapMutex sync.RWMutex
func getStructInfo(st reflect.Type) (*structInfo, error) {
fieldMapMutex.RLock()
sinfo, found := structMap[st]
fieldMapMutex.RUnlock()
if found {
return sinfo, nil
}
n := st.NumField()
fieldsMap := make(map[string]fieldInfo)
fieldsList := make([]fieldInfo, 0, n)
inlineMap := -1
for i := 0; i != n; i++ {
field := st.Field(i)
if field.PkgPath != "" {
continue // Private field
}
info := fieldInfo{Num: i}
tag := field.Tag.Get("yaml")
if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
tag = string(field.Tag)
}
if tag == "-" {
continue
}
inline := false
fields := strings.Split(tag, ",")
if len(fields) > 1 {
for _, flag := range fields[1:] {
switch flag {
case "omitempty":
info.OmitEmpty = true
case "flow":
info.Flow = true
case "inline":
inline = true
default:
return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st))
}
}
tag = fields[0]
}
if inline {
switch field.Type.Kind() {
case reflect.Map:
if inlineMap >= 0 {
return nil, errors.New("Multiple ,inline maps in struct " + st.String())
}
if field.Type.Key() != reflect.TypeOf("") {
return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
}
inlineMap = info.Num
case reflect.Struct:
sinfo, err := getStructInfo(field.Type)
if err != nil {
return nil, err
}
for _, finfo := range sinfo.FieldsList {
if _, found := fieldsMap[finfo.Key]; found {
msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
return nil, errors.New(msg)
}
if finfo.Inline == nil {
finfo.Inline = []int{i, finfo.Num}
} else {
finfo.Inline = append([]int{i}, finfo.Inline...)
}
fieldsMap[finfo.Key] = finfo
fieldsList = append(fieldsList, finfo)
}
default:
//return nil, errors.New("Option ,inline needs a struct value or map field")
return nil, errors.New("Option ,inline needs a struct value field")
}
continue
}
if tag != "" {
info.Key = tag
} else {
info.Key = strings.ToLower(field.Name)
}
if _, found = fieldsMap[info.Key]; found {
msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
return nil, errors.New(msg)
}
fieldsList = append(fieldsList, info)
fieldsMap[info.Key] = info
}
sinfo = &structInfo{fieldsMap, fieldsList, inlineMap}
fieldMapMutex.Lock()
structMap[st] = sinfo
fieldMapMutex.Unlock()
return sinfo, nil
}
func isZero(v reflect.Value) bool {
switch v.Kind() {
case reflect.String:
return len(v.String()) == 0
case reflect.Interface, reflect.Ptr:
return v.IsNil()
case reflect.Slice:
return v.Len() == 0
case reflect.Map:
return v.Len() == 0
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Struct:
vt := v.Type()
for i := v.NumField() - 1; i >= 0; i-- {
if vt.Field(i).PkgPath != "" {
continue // Private field
}
if !isZero(v.Field(i)) {
return false
}
}
return true
}
return false
}

716
Godeps/_workspace/src/gopkg.in/yaml.v2/yamlh.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,716 @@
package yaml
import (
"io"
)
// The version directive data.
type yaml_version_directive_t struct {
major int8 // The major version number.
minor int8 // The minor version number.
}
// The tag directive data.
type yaml_tag_directive_t struct {
handle []byte // The tag handle.
prefix []byte // The tag prefix.
}
type yaml_encoding_t int
// The stream encoding.
const (
// Let the parser choose the encoding.
yaml_ANY_ENCODING yaml_encoding_t = iota
yaml_UTF8_ENCODING // The default UTF-8 encoding.
yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM.
yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM.
)
type yaml_break_t int
// Line break types.
const (
// Let the parser choose the break type.
yaml_ANY_BREAK yaml_break_t = iota
yaml_CR_BREAK // Use CR for line breaks (Mac style).
yaml_LN_BREAK // Use LN for line breaks (Unix style).
yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style).
)
type yaml_error_type_t int
// Many bad things could happen with the parser and emitter.
const (
// No error is produced.
yaml_NO_ERROR yaml_error_type_t = iota
yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory.
yaml_READER_ERROR // Cannot read or decode the input stream.
yaml_SCANNER_ERROR // Cannot scan the input stream.
yaml_PARSER_ERROR // Cannot parse the input stream.
yaml_COMPOSER_ERROR // Cannot compose a YAML document.
yaml_WRITER_ERROR // Cannot write to the output stream.
yaml_EMITTER_ERROR // Cannot emit a YAML stream.
)
// The pointer position.
type yaml_mark_t struct {
index int // The position index.
line int // The position line.
column int // The position column.
}
// Node Styles
type yaml_style_t int8
type yaml_scalar_style_t yaml_style_t
// Scalar styles.
const (
// Let the emitter choose the style.
yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = iota
yaml_PLAIN_SCALAR_STYLE // The plain scalar style.
yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style.
yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style.
yaml_LITERAL_SCALAR_STYLE // The literal scalar style.
yaml_FOLDED_SCALAR_STYLE // The folded scalar style.
)
type yaml_sequence_style_t yaml_style_t
// Sequence styles.
const (
// Let the emitter choose the style.
yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota
yaml_BLOCK_SEQUENCE_STYLE // The block sequence style.
yaml_FLOW_SEQUENCE_STYLE // The flow sequence style.
)
type yaml_mapping_style_t yaml_style_t
// Mapping styles.
const (
// Let the emitter choose the style.
yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota
yaml_BLOCK_MAPPING_STYLE // The block mapping style.
yaml_FLOW_MAPPING_STYLE // The flow mapping style.
)
// Tokens
type yaml_token_type_t int
// Token types.
const (
// An empty token.
yaml_NO_TOKEN yaml_token_type_t = iota
yaml_STREAM_START_TOKEN // A STREAM-START token.
yaml_STREAM_END_TOKEN // A STREAM-END token.
yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token.
yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token.
yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token.
yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token.
yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token.
yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token.
yaml_BLOCK_END_TOKEN // A BLOCK-END token.
yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token.
yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token.
yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token.
yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token.
yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token.
yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token.
yaml_KEY_TOKEN // A KEY token.
yaml_VALUE_TOKEN // A VALUE token.
yaml_ALIAS_TOKEN // An ALIAS token.
yaml_ANCHOR_TOKEN // An ANCHOR token.
yaml_TAG_TOKEN // A TAG token.
yaml_SCALAR_TOKEN // A SCALAR token.
)
func (tt yaml_token_type_t) String() string {
switch tt {
case yaml_NO_TOKEN:
return "yaml_NO_TOKEN"
case yaml_STREAM_START_TOKEN:
return "yaml_STREAM_START_TOKEN"
case yaml_STREAM_END_TOKEN:
return "yaml_STREAM_END_TOKEN"
case yaml_VERSION_DIRECTIVE_TOKEN:
return "yaml_VERSION_DIRECTIVE_TOKEN"
case yaml_TAG_DIRECTIVE_TOKEN:
return "yaml_TAG_DIRECTIVE_TOKEN"
case yaml_DOCUMENT_START_TOKEN:
return "yaml_DOCUMENT_START_TOKEN"
case yaml_DOCUMENT_END_TOKEN:
return "yaml_DOCUMENT_END_TOKEN"
case yaml_BLOCK_SEQUENCE_START_TOKEN:
return "yaml_BLOCK_SEQUENCE_START_TOKEN"
case yaml_BLOCK_MAPPING_START_TOKEN:
return "yaml_BLOCK_MAPPING_START_TOKEN"
case yaml_BLOCK_END_TOKEN:
return "yaml_BLOCK_END_TOKEN"
case yaml_FLOW_SEQUENCE_START_TOKEN:
return "yaml_FLOW_SEQUENCE_START_TOKEN"
case yaml_FLOW_SEQUENCE_END_TOKEN:
return "yaml_FLOW_SEQUENCE_END_TOKEN"
case yaml_FLOW_MAPPING_START_TOKEN:
return "yaml_FLOW_MAPPING_START_TOKEN"
case yaml_FLOW_MAPPING_END_TOKEN:
return "yaml_FLOW_MAPPING_END_TOKEN"
case yaml_BLOCK_ENTRY_TOKEN:
return "yaml_BLOCK_ENTRY_TOKEN"
case yaml_FLOW_ENTRY_TOKEN:
return "yaml_FLOW_ENTRY_TOKEN"
case yaml_KEY_TOKEN:
return "yaml_KEY_TOKEN"
case yaml_VALUE_TOKEN:
return "yaml_VALUE_TOKEN"
case yaml_ALIAS_TOKEN:
return "yaml_ALIAS_TOKEN"
case yaml_ANCHOR_TOKEN:
return "yaml_ANCHOR_TOKEN"
case yaml_TAG_TOKEN:
return "yaml_TAG_TOKEN"
case yaml_SCALAR_TOKEN:
return "yaml_SCALAR_TOKEN"
}
return "<unknown token>"
}
// The token structure.
type yaml_token_t struct {
// The token type.
typ yaml_token_type_t
// The start/end of the token.
start_mark, end_mark yaml_mark_t
// The stream encoding (for yaml_STREAM_START_TOKEN).
encoding yaml_encoding_t
// The alias/anchor/scalar value or tag/tag directive handle
// (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN).
value []byte
// The tag suffix (for yaml_TAG_TOKEN).
suffix []byte
// The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN).
prefix []byte
// The scalar style (for yaml_SCALAR_TOKEN).
style yaml_scalar_style_t
// The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN).
major, minor int8
}
// Events
type yaml_event_type_t int8
// Event types.
const (
// An empty event.
yaml_NO_EVENT yaml_event_type_t = iota
yaml_STREAM_START_EVENT // A STREAM-START event.
yaml_STREAM_END_EVENT // A STREAM-END event.
yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event.
yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event.
yaml_ALIAS_EVENT // An ALIAS event.
yaml_SCALAR_EVENT // A SCALAR event.
yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event.
yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event.
yaml_MAPPING_START_EVENT // A MAPPING-START event.
yaml_MAPPING_END_EVENT // A MAPPING-END event.
)
// The event structure.
type yaml_event_t struct {
// The event type.
typ yaml_event_type_t
// The start and end of the event.
start_mark, end_mark yaml_mark_t
// The document encoding (for yaml_STREAM_START_EVENT).
encoding yaml_encoding_t
// The version directive (for yaml_DOCUMENT_START_EVENT).
version_directive *yaml_version_directive_t
// The list of tag directives (for yaml_DOCUMENT_START_EVENT).
tag_directives []yaml_tag_directive_t
// The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT).
anchor []byte
// The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT).
tag []byte
// The scalar value (for yaml_SCALAR_EVENT).
value []byte
// Is the document start/end indicator implicit, or the tag optional?
// (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT).
implicit bool
// Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT).
quoted_implicit bool
// The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT).
style yaml_style_t
}
func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) }
func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) }
func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) }
// Nodes
const (
yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null.
yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false.
yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values.
yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values.
yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values.
yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values.
yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences.
yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping.
// Not in original libyaml.
yaml_BINARY_TAG = "tag:yaml.org,2002:binary"
yaml_MERGE_TAG = "tag:yaml.org,2002:merge"
yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str.
yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq.
yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map.
)
type yaml_node_type_t int
// Node types.
const (
// An empty node.
yaml_NO_NODE yaml_node_type_t = iota
yaml_SCALAR_NODE // A scalar node.
yaml_SEQUENCE_NODE // A sequence node.
yaml_MAPPING_NODE // A mapping node.
)
// An element of a sequence node.
type yaml_node_item_t int
// An element of a mapping node.
type yaml_node_pair_t struct {
key int // The key of the element.
value int // The value of the element.
}
// The node structure.
type yaml_node_t struct {
typ yaml_node_type_t // The node type.
tag []byte // The node tag.
// The node data.
// The scalar parameters (for yaml_SCALAR_NODE).
scalar struct {
value []byte // The scalar value.
length int // The length of the scalar value.
style yaml_scalar_style_t // The scalar style.
}
// The sequence parameters (for YAML_SEQUENCE_NODE).
sequence struct {
items_data []yaml_node_item_t // The stack of sequence items.
style yaml_sequence_style_t // The sequence style.
}
// The mapping parameters (for yaml_MAPPING_NODE).
mapping struct {
pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value).
pairs_start *yaml_node_pair_t // The beginning of the stack.
pairs_end *yaml_node_pair_t // The end of the stack.
pairs_top *yaml_node_pair_t // The top of the stack.
style yaml_mapping_style_t // The mapping style.
}
start_mark yaml_mark_t // The beginning of the node.
end_mark yaml_mark_t // The end of the node.
}
// The document structure.
type yaml_document_t struct {
// The document nodes.
nodes []yaml_node_t
// The version directive.
version_directive *yaml_version_directive_t
// The list of tag directives.
tag_directives_data []yaml_tag_directive_t
tag_directives_start int // The beginning of the tag directives list.
tag_directives_end int // The end of the tag directives list.
start_implicit int // Is the document start indicator implicit?
end_implicit int // Is the document end indicator implicit?
// The start/end of the document.
start_mark, end_mark yaml_mark_t
}
// The prototype of a read handler.
//
// The read handler is called when the parser needs to read more bytes from the
// source. The handler should write not more than size bytes to the buffer.
// The number of written bytes should be set to the size_read variable.
//
// [in,out] data A pointer to an application data specified by
// yaml_parser_set_input().
// [out] buffer The buffer to write the data from the source.
// [in] size The size of the buffer.
// [out] size_read The actual number of bytes read from the source.
//
// On success, the handler should return 1. If the handler failed,
// the returned value should be 0. On EOF, the handler should set the
// size_read to 0 and return 1.
type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error)
// This structure holds information about a potential simple key.
type yaml_simple_key_t struct {
possible bool // Is a simple key possible?
required bool // Is a simple key required?
token_number int // The number of the token.
mark yaml_mark_t // The position mark.
}
// The states of the parser.
type yaml_parser_state_t int
const (
yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota
yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document.
yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START.
yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document.
yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END.
yaml_PARSE_BLOCK_NODE_STATE // Expect a block node.
yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence.
yaml_PARSE_FLOW_NODE_STATE // Expect a flow node.
yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence.
yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence.
yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence.
yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping.
yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key.
yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value.
yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence.
yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence.
yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping.
yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping.
yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry.
yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping.
yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping.
yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping.
yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping.
yaml_PARSE_END_STATE // Expect nothing.
)
func (ps yaml_parser_state_t) String() string {
switch ps {
case yaml_PARSE_STREAM_START_STATE:
return "yaml_PARSE_STREAM_START_STATE"
case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE:
return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE"
case yaml_PARSE_DOCUMENT_START_STATE:
return "yaml_PARSE_DOCUMENT_START_STATE"
case yaml_PARSE_DOCUMENT_CONTENT_STATE:
return "yaml_PARSE_DOCUMENT_CONTENT_STATE"
case yaml_PARSE_DOCUMENT_END_STATE:
return "yaml_PARSE_DOCUMENT_END_STATE"
case yaml_PARSE_BLOCK_NODE_STATE:
return "yaml_PARSE_BLOCK_NODE_STATE"
case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE:
return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE"
case yaml_PARSE_FLOW_NODE_STATE:
return "yaml_PARSE_FLOW_NODE_STATE"
case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE:
return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE"
case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE:
return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE"
case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE:
return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE"
case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE:
return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE"
case yaml_PARSE_BLOCK_MAPPING_KEY_STATE:
return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE"
case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE:
return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE"
case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE:
return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE"
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE:
return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE"
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE:
return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE"
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE:
return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE"
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE:
return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE"
case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE:
return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE"
case yaml_PARSE_FLOW_MAPPING_KEY_STATE:
return "yaml_PARSE_FLOW_MAPPING_KEY_STATE"
case yaml_PARSE_FLOW_MAPPING_VALUE_STATE:
return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE"
case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE:
return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE"
case yaml_PARSE_END_STATE:
return "yaml_PARSE_END_STATE"
}
return "<unknown parser state>"
}
// This structure holds aliases data.
type yaml_alias_data_t struct {
anchor []byte // The anchor.
index int // The node id.
mark yaml_mark_t // The anchor mark.
}
// The parser structure.
//
// All members are internal. Manage the structure using the
// yaml_parser_ family of functions.
type yaml_parser_t struct {
// Error handling
error yaml_error_type_t // Error type.
problem string // Error description.
// The byte about which the problem occured.
problem_offset int
problem_value int
problem_mark yaml_mark_t
// The error context.
context string
context_mark yaml_mark_t
// Reader stuff
read_handler yaml_read_handler_t // Read handler.
input_file io.Reader // File input data.
input []byte // String input data.
input_pos int
eof bool // EOF flag
buffer []byte // The working buffer.
buffer_pos int // The current position of the buffer.
unread int // The number of unread characters in the buffer.
raw_buffer []byte // The raw buffer.
raw_buffer_pos int // The current position of the buffer.
encoding yaml_encoding_t // The input encoding.
offset int // The offset of the current position (in bytes).
mark yaml_mark_t // The mark of the current position.
// Scanner stuff
stream_start_produced bool // Have we started to scan the input stream?
stream_end_produced bool // Have we reached the end of the input stream?
flow_level int // The number of unclosed '[' and '{' indicators.
tokens []yaml_token_t // The tokens queue.
tokens_head int // The head of the tokens queue.
tokens_parsed int // The number of tokens fetched from the queue.
token_available bool // Does the tokens queue contain a token ready for dequeueing.
indent int // The current indentation level.
indents []int // The indentation levels stack.
simple_key_allowed bool // May a simple key occur at the current position?
simple_keys []yaml_simple_key_t // The stack of simple keys.
// Parser stuff
state yaml_parser_state_t // The current parser state.
states []yaml_parser_state_t // The parser states stack.
marks []yaml_mark_t // The stack of marks.
tag_directives []yaml_tag_directive_t // The list of TAG directives.
// Dumper stuff
aliases []yaml_alias_data_t // The alias data.
document *yaml_document_t // The currently parsed document.
}
// Emitter Definitions
// The prototype of a write handler.
//
// The write handler is called when the emitter needs to flush the accumulated
// characters to the output. The handler should write @a size bytes of the
// @a buffer to the output.
//
// @param[in,out] data A pointer to an application data specified by
// yaml_emitter_set_output().
// @param[in] buffer The buffer with bytes to be written.
// @param[in] size The size of the buffer.
//
// @returns On success, the handler should return @c 1. If the handler failed,
// the returned value should be @c 0.
//
type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error
type yaml_emitter_state_t int
// The emitter states.
const (
// Expect STREAM-START.
yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota
yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END.
yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END.
yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document.
yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END.
yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence.
yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence.
yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping.
yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping.
yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping.
yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping.
yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence.
yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence.
yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping.
yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping.
yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping.
yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping.
yaml_EMIT_END_STATE // Expect nothing.
)
// The emitter structure.
//
// All members are internal. Manage the structure using the @c yaml_emitter_
// family of functions.
type yaml_emitter_t struct {
// Error handling
error yaml_error_type_t // Error type.
problem string // Error description.
// Writer stuff
write_handler yaml_write_handler_t // Write handler.
output_buffer *[]byte // String output data.
output_file io.Writer // File output data.
buffer []byte // The working buffer.
buffer_pos int // The current position of the buffer.
raw_buffer []byte // The raw buffer.
raw_buffer_pos int // The current position of the buffer.
encoding yaml_encoding_t // The stream encoding.
// Emitter stuff
canonical bool // If the output is in the canonical style?
best_indent int // The number of indentation spaces.
best_width int // The preferred width of the output lines.
unicode bool // Allow unescaped non-ASCII characters?
line_break yaml_break_t // The preferred line break.
state yaml_emitter_state_t // The current emitter state.
states []yaml_emitter_state_t // The stack of states.
events []yaml_event_t // The event queue.
events_head int // The head of the event queue.
indents []int // The stack of indentation levels.
tag_directives []yaml_tag_directive_t // The list of tag directives.
indent int // The current indentation level.
flow_level int // The current flow level.
root_context bool // Is it the document root context?
sequence_context bool // Is it a sequence context?
mapping_context bool // Is it a mapping context?
simple_key_context bool // Is it a simple mapping key context?
line int // The current line.
column int // The current column.
whitespace bool // If the last character was a whitespace?
indention bool // If the last character was an indentation character (' ', '-', '?', ':')?
open_ended bool // If an explicit document end is required?
// Anchor analysis.
anchor_data struct {
anchor []byte // The anchor value.
alias bool // Is it an alias?
}
// Tag analysis.
tag_data struct {
handle []byte // The tag handle.
suffix []byte // The tag suffix.
}
// Scalar analysis.
scalar_data struct {
value []byte // The scalar value.
multiline bool // Does the scalar contain line breaks?
flow_plain_allowed bool // Can the scalar be expessed in the flow plain style?
block_plain_allowed bool // Can the scalar be expressed in the block plain style?
single_quoted_allowed bool // Can the scalar be expressed in the single quoted style?
block_allowed bool // Can the scalar be expressed in the literal or folded styles?
style yaml_scalar_style_t // The output style.
}
// Dumper stuff
opened bool // If the stream was already opened?
closed bool // If the stream was already closed?
// The information associated with the document nodes.
anchors *struct {
references int // The number of references.
anchor int // The anchor id.
serialized bool // If the node has been emitted?
}
last_anchor_id int // The last assigned anchor id.
document *yaml_document_t // The currently emitted document.
}

173
Godeps/_workspace/src/gopkg.in/yaml.v2/yamlprivateh.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,173 @@
package yaml
const (
// The size of the input raw buffer.
input_raw_buffer_size = 512
// The size of the input buffer.
// It should be possible to decode the whole raw buffer.
input_buffer_size = input_raw_buffer_size * 3
// The size of the output buffer.
output_buffer_size = 128
// The size of the output raw buffer.
// It should be possible to encode the whole output buffer.
output_raw_buffer_size = (output_buffer_size*2 + 2)
// The size of other stacks and queues.
initial_stack_size = 16
initial_queue_size = 16
initial_string_size = 16
)
// Check if the character at the specified position is an alphabetical
// character, a digit, '_', or '-'.
func is_alpha(b []byte, i int) bool {
return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-'
}
// Check if the character at the specified position is a digit.
func is_digit(b []byte, i int) bool {
return b[i] >= '0' && b[i] <= '9'
}
// Get the value of a digit.
func as_digit(b []byte, i int) int {
return int(b[i]) - '0'
}
// Check if the character at the specified position is a hex-digit.
func is_hex(b []byte, i int) bool {
return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f'
}
// Get the value of a hex-digit.
func as_hex(b []byte, i int) int {
bi := b[i]
if bi >= 'A' && bi <= 'F' {
return int(bi) - 'A' + 10
}
if bi >= 'a' && bi <= 'f' {
return int(bi) - 'a' + 10
}
return int(bi) - '0'
}
// Check if the character is ASCII.
func is_ascii(b []byte, i int) bool {
return b[i] <= 0x7F
}
// Check if the character at the start of the buffer can be printed unescaped.
func is_printable(b []byte, i int) bool {
return ((b[i] == 0x0A) || // . == #x0A
(b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E
(b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF
(b[i] > 0xC2 && b[i] < 0xED) ||
(b[i] == 0xED && b[i+1] < 0xA0) ||
(b[i] == 0xEE) ||
(b[i] == 0xEF && // #xE000 <= . <= #xFFFD
!(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF
!(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF))))
}
// Check if the character at the specified position is NUL.
func is_z(b []byte, i int) bool {
return b[i] == 0x00
}
// Check if the beginning of the buffer is a BOM.
func is_bom(b []byte, i int) bool {
return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF
}
// Check if the character at the specified position is space.
func is_space(b []byte, i int) bool {
return b[i] == ' '
}
// Check if the character at the specified position is tab.
func is_tab(b []byte, i int) bool {
return b[i] == '\t'
}
// Check if the character at the specified position is blank (space or tab).
func is_blank(b []byte, i int) bool {
//return is_space(b, i) || is_tab(b, i)
return b[i] == ' ' || b[i] == '\t'
}
// Check if the character at the specified position is a line break.
func is_break(b []byte, i int) bool {
return (b[i] == '\r' || // CR (#xD)
b[i] == '\n' || // LF (#xA)
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029)
}
func is_crlf(b []byte, i int) bool {
return b[i] == '\r' && b[i+1] == '\n'
}
// Check if the character is a line break or NUL.
func is_breakz(b []byte, i int) bool {
//return is_break(b, i) || is_z(b, i)
return ( // is_break:
b[i] == '\r' || // CR (#xD)
b[i] == '\n' || // LF (#xA)
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029)
// is_z:
b[i] == 0)
}
// Check if the character is a line break, space, or NUL.
func is_spacez(b []byte, i int) bool {
//return is_space(b, i) || is_breakz(b, i)
return ( // is_space:
b[i] == ' ' ||
// is_breakz:
b[i] == '\r' || // CR (#xD)
b[i] == '\n' || // LF (#xA)
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029)
b[i] == 0)
}
// Check if the character is a line break, space, tab, or NUL.
func is_blankz(b []byte, i int) bool {
//return is_blank(b, i) || is_breakz(b, i)
return ( // is_blank:
b[i] == ' ' || b[i] == '\t' ||
// is_breakz:
b[i] == '\r' || // CR (#xD)
b[i] == '\n' || // LF (#xA)
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029)
b[i] == 0)
}
// Determine the width of the character.
func width(b byte) int {
// Don't replace these by a switch without first
// confirming that it is being inlined.
if b&0x80 == 0x00 {
return 1
}
if b&0xE0 == 0xC0 {
return 2
}
if b&0xF0 == 0xE0 {
return 3
}
if b&0xF8 == 0xF0 {
return 4
}
return 0
}

Просмотреть файл

@@ -118,6 +118,9 @@ package:
mkdir -p $(DIST_PATH)/web/static/js
cp -L web/static/js/*.min.js $(DIST_PATH)/web/static/js/
cp -L web/static/js/*.min.js.map $(DIST_PATH)/web/static/js/
cp -RL web/static/js/intl-1.0.0 $(DIST_PATH)/web/static/js/
cp -RL web/static/js/react-intl-2.0.0-beta-2 $(DIST_PATH)/web/static/js/
cp -RL web/static/i18n $(DIST_PATH)/web/static
cp -RL web/static/config $(DIST_PATH)/web/static
cp -RL web/static/css $(DIST_PATH)/web/static
cp -RL web/static/fonts $(DIST_PATH)/web/static
@@ -128,6 +131,7 @@ package:
mkdir -p $(DIST_PATH)/api
cp -RL api/templates $(DIST_PATH)/api
cp -RL i18n $(DIST_PATH)
cp build/MIT-COMPILED-LICENSE.md $(DIST_PATH)
cp NOTICE.txt $(DIST_PATH)
@@ -138,6 +142,8 @@ package:
sed -i'.bak' 's|react-0.14.3.js|react-0.14.3.min.js|g' $(DIST_PATH)/web/templates/head.html
sed -i'.bak' 's|react-dom-0.14.3.js|react-dom-0.14.3.min.js|g' $(DIST_PATH)/web/templates/head.html
sed -i'.bak' 's|Intl.js|Intl.min.js|g' $(DIST_PATH)/web/templates/head.html
sed -i'.bak' 's|react-intl.js|react-intl.min.js|g' $(DIST_PATH)/web/templates/head.html
sed -i'.bak' 's|jquery-2.1.4.js|jquery-2.1.4.min.js|g' $(DIST_PATH)/web/templates/head.html
sed -i'.bak' 's|bootstrap-3.3.5.js|bootstrap-3.3.5.min.js|g' $(DIST_PATH)/web/templates/head.html
sed -i'.bak' 's|react-bootstrap-0.28.1.js|react-bootstrap-0.28.1.min.js|g' $(DIST_PATH)/web/templates/head.html
@@ -145,6 +151,8 @@ package:
sed -i'.bak' 's|bundle.js|bundle-$(BUILD_NUMBER).min.js|g' $(DIST_PATH)/web/templates/head.html
sed -i'.bak' 's|libs.min.js|libs-$(BUILD_NUMBER).min.js|g' $(DIST_PATH)/web/templates/head.html
rm $(DIST_PATH)/web/templates/*.bak
sudo mv -f $(DIST_PATH)/config/config.json.bak $(DIST_PATH)/config/config.json || echo 'nomv'
tar -C dist -czf $(DIST_PATH).tar.gz mattermost
@@ -179,7 +187,7 @@ travis-init:
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@postgres:5432/mattermost_test?sslmode=disable\&connect_timeout=10|g' config/config.json; \
sed -i'.bak2' 's|mmuser:mostest@tcp(dockerhost:3306)/mattermost_test?charset=utf8mb4,utf8|postgres://mmuser:mostest@postgres:5432/mattermost_test?sslmode=disable\&connect_timeout=10|g' config/config.json; \
fi
if [ "$(TRAVIS_DB)" = "mysql" ]; then \

Просмотреть файл

@@ -17,7 +17,7 @@ import (
)
func InitAdmin(r *mux.Router) {
l4g.Debug("Initializing admin api routes")
l4g.Debug(utils.T("api.admin.init.debug"))
sr := r.PathPrefix("/admin").Subrouter()
sr.Handle("/logs", ApiUserRequired(getLogs)).Methods("GET")
@@ -41,7 +41,7 @@ func getLogs(c *Context, w http.ResponseWriter, r *http.Request) {
file, err := os.Open(utils.GetLogFileLocation(utils.Cfg.LogSettings.FileLocation))
if err != nil {
c.Err = model.NewAppError("getLogs", "Error reading log file", err.Error())
c.Err = model.NewLocAppError("getLogs", "api.admin.file_read_error", nil, err.Error())
}
defer file.Close()
@@ -132,7 +132,7 @@ func testEmail(c *Context, w http.ResponseWriter, r *http.Request) {
c.Err = result.Err
return
} else {
if err := utils.SendMailUsingConfig(result.Data.(*model.User).Email, "Mattermost - Testing Email Settings", "<br/><br/><br/>It appears your Mattermost email is setup correctly!", cfg); err != nil {
if err := utils.SendMailUsingConfig(result.Data.(*model.User).Email, c.T("api.admin.test_email.subject"), c.T("api.admin.test_email.body"), cfg); err != nil {
c.Err = err
return
}

Просмотреть файл

@@ -10,6 +10,9 @@ import (
"github.com/mattermost/platform/utils"
"html/template"
"net/http"
_ "github.com/cloudfoundry/jibber_jabber"
_ "github.com/nicksnyder/go-i18n/i18n"
)
var ServerTemplates *template.Template
@@ -21,13 +24,14 @@ func NewServerTemplatePage(templateName string) *ServerTemplatePage {
TemplateName: templateName,
Props: make(map[string]string),
ClientCfg: utils.ClientCfg,
Locale: model.DEFAULT_LOCALE,
}
}
func (me *ServerTemplatePage) Render() string {
var text bytes.Buffer
if err := ServerTemplates.ExecuteTemplate(&text, me.TemplateName, me); err != nil {
l4g.Error("Error rendering template %v err=%v", me.TemplateName, err)
l4g.Error(utils.T("api.api.render.error"), me.TemplateName, err)
}
return text.String()
@@ -49,10 +53,10 @@ func InitApi() {
InitLicense(r)
templatesDir := utils.FindDir("api/templates")
l4g.Debug("Parsing server templates at %v", templatesDir)
l4g.Debug(utils.T("api.api.init.parsing_templates.debug"), templatesDir)
var err error
if ServerTemplates, err = template.ParseGlob(templatesDir + "*.html"); err != nil {
l4g.Error("Failed to parse server templates %v", err)
l4g.Error(utils.T("api.api.init.parsing_templates.error"), err)
}
}

Просмотреть файл

@@ -14,6 +14,7 @@ var Client *model.Client
func Setup() {
if Srv == nil {
utils.LoadConfig("config.json")
utils.InitTranslations()
utils.Cfg.TeamSettings.MaxUsersPerTeam = 50
NewServer()
StartServer()

Просмотреть файл

@@ -8,6 +8,7 @@ import (
l4g "github.com/alecthomas/log4go"
"github.com/gorilla/mux"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
"net/http"
"strconv"
"strings"
@@ -18,7 +19,7 @@ const (
)
func InitChannel(r *mux.Router) {
l4g.Debug("Initializing channel api routes")
l4g.Debug(utils.T("api.channel.init.debug"))
sr := r.PathPrefix("/channels").Subrouter()
sr.Handle("/", ApiUserRequiredActivity(getChannels, false)).Methods("GET")
@@ -56,12 +57,12 @@ func createChannel(c *Context, w http.ResponseWriter, r *http.Request) {
}
if channel.Type == model.CHANNEL_DIRECT {
c.Err = model.NewAppError("createDirectChannel", "Must use createDirectChannel api service for direct message channel creation", "")
c.Err = model.NewLocAppError("createDirectChannel", "api.channel.create_channel.direct_channel.app_error", nil, "")
return
}
if strings.Index(channel.Name, "__") > 0 {
c.Err = model.NewAppError("createDirectChannel", "Invalid character '__' in channel name for non-direct channel", "")
c.Err = model.NewLocAppError("createDirectChannel", "api.channel.create_channel.invalid_character.app_error", nil, "")
return
}
@@ -120,7 +121,7 @@ func createDirectChannel(c *Context, w http.ResponseWriter, r *http.Request) {
func CreateDirectChannel(c *Context, otherUserId string) (*model.Channel, *model.AppError) {
if len(otherUserId) != 26 {
return nil, model.NewAppError("CreateDirectChannel", "Invalid other user id ", otherUserId)
return nil, model.NewLocAppError("CreateDirectChannel", "api.channel.create_direct_channel.invalid_user.app_error", nil, otherUserId)
}
uc := Srv.Store.User().Get(otherUserId)
@@ -135,7 +136,7 @@ func CreateDirectChannel(c *Context, otherUserId string) (*model.Channel, *model
channel.Type = model.CHANNEL_DIRECT
if uresult := <-uc; uresult.Err != nil {
return nil, model.NewAppError("CreateDirectChannel", "Invalid other user id ", otherUserId)
return nil, model.NewLocAppError("CreateDirectChannel", "api.channel.create_direct_channel.invalid_user.app_error", nil, otherUserId)
}
cm1 := &model.ChannelMember{
@@ -157,13 +158,13 @@ func CreateDirectChannel(c *Context, otherUserId string) (*model.Channel, *model
}
func CreateDefaultChannels(c *Context, teamId string) ([]*model.Channel, *model.AppError) {
townSquare := &model.Channel{DisplayName: "Town Square", Name: "town-square", Type: model.CHANNEL_OPEN, TeamId: teamId}
townSquare := &model.Channel{DisplayName: c.T("api.channel.create_default_channels.town_square"), Name: "town-square", Type: model.CHANNEL_OPEN, TeamId: teamId}
if _, err := CreateChannel(c, townSquare, false); err != nil {
return nil, err
}
offTopic := &model.Channel{DisplayName: "Off-Topic", Name: "off-topic", Type: model.CHANNEL_OPEN, TeamId: teamId}
offTopic := &model.Channel{DisplayName: c.T("api.channel.create_default_channels.off_topic"), Name: "off-topic", Type: model.CHANNEL_OPEN, TeamId: teamId}
if _, err := CreateChannel(c, offTopic, false); err != nil {
return nil, err
@@ -199,20 +200,20 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) {
}
if !strings.Contains(channelMember.Roles, model.CHANNEL_ROLE_ADMIN) && !strings.Contains(c.Session.Roles, model.ROLE_TEAM_ADMIN) {
c.Err = model.NewAppError("updateChannel", "You do not have the appropriate permissions", "")
c.Err = model.NewLocAppError("updateChannel", "api.channel.update_channel.permission.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
if oldChannel.DeleteAt > 0 {
c.Err = model.NewAppError("updateChannel", "The channel has been archived or deleted", "")
c.Err = model.NewLocAppError("updateChannel", "api.channel.update_channel.deleted.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}
if oldChannel.Name == model.DEFAULT_CHANNEL {
if (len(channel.Name) > 0 && channel.Name != oldChannel.Name) || (len(channel.Type) > 0 && channel.Type != oldChannel.Type) {
c.Err = model.NewAppError("updateChannel", "Tried to perform an invalid update of the default channel "+model.DEFAULT_CHANNEL, "")
c.Err = model.NewLocAppError("updateChannel", "api.channel.update_channel.tried.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}, "")
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -293,18 +294,18 @@ func PostUpdateChannelHeaderMessageAndForget(c *Context, channelId string, oldCh
uc := Srv.Store.User().Get(c.Session.UserId)
if uresult := <-uc; uresult.Err != nil {
l4g.Error("Failed to retrieve user while trying to save update channel header message %v", uresult.Err)
l4g.Error(utils.T("api.channel.post_update_channel_header_message_and_forget.retrieve_user.error"), uresult.Err)
return
} else {
user := uresult.Data.(*model.User)
var message string
if oldChannelHeader == "" {
message = fmt.Sprintf("%s updated the channel header to: %s", user.Username, newChannelHeader)
message = fmt.Sprintf(c.T("api.channel.post_update_channel_header_message_and_forget.updated_to"), user.Username, newChannelHeader)
} else if newChannelHeader == "" {
message = fmt.Sprintf("%s removed the channel header (was: %s)", user.Username, oldChannelHeader)
message = fmt.Sprintf(c.T("api.channel.post_update_channel_header_message_and_forget.removed"), user.Username, oldChannelHeader)
} else {
message = fmt.Sprintf("%s updated the channel header from: %s to: %s", user.Username, oldChannelHeader, newChannelHeader)
message = fmt.Sprintf(c.T("api.channel.post_update_channel_header_message_and_forget.updated_from"), user.Username, oldChannelHeader, newChannelHeader)
}
post := &model.Post{
@@ -313,7 +314,7 @@ func PostUpdateChannelHeaderMessageAndForget(c *Context, channelId string, oldCh
Type: model.POST_HEADER_CHANGE,
}
if _, err := CreatePost(c, post, false); err != nil {
l4g.Error("Failed to post join/leave message %v", err)
l4g.Error(utils.T("api.channel.post_update_channel_header_message_and_forget.join_leave.error"), err)
}
}
}()
@@ -367,12 +368,12 @@ func getChannels(c *Context, w http.ResponseWriter, r *http.Request) {
// user is already in the team
if result := <-Srv.Store.Channel().GetChannels(c.Session.TeamId, c.Session.UserId); result.Err != nil {
if result.Err.Message == "No channels were found" {
if result.Err.Message == "No channels were found" { // store translation dependant
// lets make sure the user is valid
if result := <-Srv.Store.User().Get(c.Session.UserId); result.Err != nil {
c.Err = result.Err
c.RemoveSessionCookie(w, r)
l4g.Error("Error in getting users profile for id=%v forcing logout", c.Session.UserId)
l4g.Error(utils.T("api.channel.get_channels.error"), c.Session.UserId)
return
}
}
@@ -408,7 +409,7 @@ func getChannelCounts(c *Context, w http.ResponseWriter, r *http.Request) {
// user is already in the team
if result := <-Srv.Store.Channel().GetChannelCounts(c.Session.TeamId, c.Session.UserId); result.Err != nil {
c.Err = model.NewAppError("getChannelCounts", "Unable to get channel counts from the database", result.Err.Message)
c.Err = model.NewLocAppError("getChannelCounts", "api.channel.get_channel_counts.app_error", nil, result.Err.Message)
return
} else if HandleEtag(result.Data.(*model.ChannelCounts).Etag(), w, r) {
return
@@ -459,9 +460,9 @@ func JoinChannel(c *Context, channelId string, role string) {
c.Err = err
return
}
PostUserAddRemoveMessageAndForget(c, channel.Id, fmt.Sprintf(`%v has joined the channel.`, user.Username))
PostUserAddRemoveMessageAndForget(c, channel.Id, fmt.Sprintf(c.T("api.channel.join_channel.post_and_forget"), user.Username))
} else {
c.Err = model.NewAppError("join", "You do not have the appropriate permissions", "")
c.Err = model.NewLocAppError("join", "api.channel.join_channel.permissions.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -476,24 +477,24 @@ func PostUserAddRemoveMessageAndForget(c *Context, channelId string, message str
Type: model.POST_JOIN_LEAVE,
}
if _, err := CreatePost(c, post, false); err != nil {
l4g.Error("Failed to post join/leave message %v", err)
l4g.Error(utils.T("api.channel.post_user_add_remove_message_and_forget.error"), err)
}
}()
}
func AddUserToChannel(user *model.User, channel *model.Channel) (*model.ChannelMember, *model.AppError) {
if channel.DeleteAt > 0 {
return nil, model.NewAppError("AddUserToChannel", "The channel has been archived or deleted", "")
return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user_to_channel.deleted.app_error", nil, "")
}
if channel.Type != model.CHANNEL_OPEN && channel.Type != model.CHANNEL_PRIVATE {
return nil, model.NewAppError("AddUserToChannel", "Can not add user to this channel type", "")
return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user_to_channel.type.app_error", nil, "")
}
newMember := &model.ChannelMember{ChannelId: channel.Id, UserId: user.Id, NotifyProps: model.GetDefaultChannelNotifyProps()}
if cmresult := <-Srv.Store.Channel().SaveMember(newMember); cmresult.Err != nil {
l4g.Error("Failed to add member user_id=%v channel_id=%v err=%v", user.Id, channel.Id, cmresult.Err)
return nil, model.NewAppError("AddUserToChannel", "Failed to add user to channel", "")
return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user.to.channel.failed.app_error", nil, "")
}
go func() {
@@ -559,13 +560,13 @@ func leave(c *Context, w http.ResponseWriter, r *http.Request) {
}
if channel.Type == model.CHANNEL_DIRECT {
c.Err = model.NewAppError("leave", "Cannot leave a direct message channel", "")
c.Err = model.NewLocAppError("leave", "api.channel.leave.direct.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
if channel.Name == model.DEFAULT_CHANNEL {
c.Err = model.NewAppError("leave", "Cannot leave the default channel "+model.DEFAULT_CHANNEL, "")
c.Err = model.NewLocAppError("leave", "api.channel.leave.default.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}, "")
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -577,7 +578,7 @@ func leave(c *Context, w http.ResponseWriter, r *http.Request) {
RemoveUserFromChannel(c.Session.UserId, c.Session.UserId, channel)
PostUserAddRemoveMessageAndForget(c, channel.Id, fmt.Sprintf(`%v has left the channel.`, user.Username))
PostUserAddRemoveMessageAndForget(c, channel.Id, fmt.Sprintf(c.T("api.channel.leave.left"), user.Username))
result := make(map[string]string)
result["id"] = channel.Id
@@ -623,19 +624,19 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) {
}
if !strings.Contains(channelMember.Roles, model.CHANNEL_ROLE_ADMIN) && !strings.Contains(c.Session.Roles, model.ROLE_TEAM_ADMIN) {
c.Err = model.NewAppError("deleteChannel", "You do not have the appropriate permissions", "")
c.Err = model.NewLocAppError("deleteChannel", "api.channel.delete_channel.permissions.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
if channel.DeleteAt > 0 {
c.Err = model.NewAppError("deleteChannel", "The channel has been archived or deleted", "")
c.Err = model.NewLocAppError("deleteChannel", "api.channel.delete_channel.deleted.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}
if channel.Name == model.DEFAULT_CHANNEL {
c.Err = model.NewAppError("deleteChannel", "Cannot delete the default channel "+model.DEFAULT_CHANNEL, "")
c.Err = model.NewLocAppError("deleteChannel", "api.channel.delete_channel.cannot.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}, "")
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -644,7 +645,7 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) {
for _, hook := range incomingHooks {
go func() {
if result := <-Srv.Store.Webhook().DeleteIncoming(hook.Id, now); result.Err != nil {
l4g.Error("Encountered error deleting incoming webhook, id=" + hook.Id)
l4g.Error(utils.T("api.channel.delete_channel.incoming_webhook.error"), hook.Id)
}
}()
}
@@ -652,7 +653,7 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) {
for _, hook := range outgoingHooks {
go func() {
if result := <-Srv.Store.Webhook().DeleteOutgoing(hook.Id, now); result.Err != nil {
l4g.Error("Encountered error deleting outgoing webhook, id=" + hook.Id)
l4g.Error(utils.T("api.channel.delete_channel.outgoing_webhook.error"), hook.Id)
}
}()
}
@@ -665,11 +666,11 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) {
c.LogAudit("name=" + channel.Name)
post := &model.Post{ChannelId: channel.Id, Message: fmt.Sprintf(
`%v has archived the channel.`,
c.T("api.channel.delete_channel.archived"),
user.Username)}
if _, err := CreatePost(c, post, false); err != nil {
l4g.Error("Failed to post archive message %v", err)
c.Err = model.NewAppError("deleteChannel", "Failed to send archive message", "")
l4g.Error(utils.T("api.channel.delete_channel.failed_post.error"), err)
c.Err = model.NewLocAppError("deleteChannel", "api.channel.delete_channel.failed_send.app_error", nil, "")
return
}
@@ -743,7 +744,7 @@ func getChannelExtraInfo(c *Context, w http.ResponseWriter, r *http.Request) {
if memberLimitString, ok := params["member_limit"]; !ok {
memberLimit = defaultExtraMemberLimit
} else if memberLimitInt64, err := strconv.ParseInt(memberLimitString, 10, 0); err != nil {
c.Err = model.NewAppError("getChannelExtraInfo", "Failed to parse member limit", err.Error())
c.Err = model.NewLocAppError("getChannelExtraInfo", "api.channel.get_channel_extra_info.member_limit.app_error", nil, err.Error())
return
} else {
memberLimit = int(memberLimitInt64)
@@ -790,7 +791,7 @@ func getChannelExtraInfo(c *Context, w http.ResponseWriter, r *http.Request) {
}
if channel.DeleteAt > 0 {
c.Err = model.NewAppError("getChannelExtraInfo", "The channel has been archived or deleted", "")
c.Err = model.NewLocAppError("getChannelExtraInfo", "api.channel.get_channel_extra_info.deleted.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}
@@ -825,17 +826,17 @@ func addMember(c *Context, w http.ResponseWriter, r *http.Request) {
}
if nresult := <-nuc; nresult.Err != nil {
c.Err = model.NewAppError("addMember", "Failed to find user to be added", "")
c.Err = model.NewLocAppError("addMember", "api.channel.add_member.find_user.app_error", nil, "")
return
} else if cresult := <-sc; cresult.Err != nil {
c.Err = model.NewAppError("addMember", "Failed to find channel", "")
c.Err = model.NewLocAppError("addMember", "api.channel.add_member.find_channel.app_error", nil, "")
return
} else {
channel := cresult.Data.(*model.Channel)
nUser := nresult.Data.(*model.User)
if oresult := <-ouc; oresult.Err != nil {
c.Err = model.NewAppError("addMember", "Failed to find user doing the adding", "")
c.Err = model.NewLocAppError("addMember", "api.channel.add_member.user_adding.app_error", nil, "")
return
} else {
oUser := oresult.Data.(*model.User)
@@ -848,7 +849,7 @@ func addMember(c *Context, w http.ResponseWriter, r *http.Request) {
c.LogAudit("name=" + channel.Name + " user_id=" + userId)
PostUserAddRemoveMessageAndForget(c, channel.Id, fmt.Sprintf(`%v added to the channel by %v`, nUser.Username, oUser.Username))
PostUserAddRemoveMessageAndForget(c, channel.Id, fmt.Sprintf(c.T("api.channel.add_member.added"), nUser.Username, oUser.Username))
<-Srv.Store.Channel().UpdateLastViewedAt(id, oUser.Id)
w.Write([]byte(cm.ToJson()))
@@ -886,13 +887,13 @@ func removeMember(c *Context, w http.ResponseWriter, r *http.Request) {
}
if !strings.Contains(removerChannelMember.Roles, model.CHANNEL_ROLE_ADMIN) && !c.IsTeamAdmin() {
c.Err = model.NewAppError("updateChannel", "You do not have the appropriate permissions ", "")
c.Err = model.NewLocAppError("updateChannel", "api.channel.remove_member.permissions.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
if err := RemoveUserFromChannel(userIdToRemove, c.Session.UserId, channel); err != nil {
c.Err = model.NewAppError("updateChannel", "Unable to remove user.", err.Message)
c.Err = model.NewLocAppError("updateChannel", "api.channel.remove_member.unable.app_error", nil, err.Message)
return
}
@@ -908,7 +909,7 @@ func removeMember(c *Context, w http.ResponseWriter, r *http.Request) {
func RemoveUserFromChannel(userIdToRemove string, removerUserId string, channel *model.Channel) *model.AppError {
if channel.DeleteAt > 0 {
return model.NewAppError("updateChannel", "The channel has been archived or deleted", "")
return model.NewLocAppError("updateChannel", "api.channel.remove_user_from_channel.deleted.app_error", nil, "")
}
if cmresult := <-Srv.Store.Channel().RemoveMember(channel.Id, userIdToRemove); cmresult.Err != nil {

Просмотреть файл

@@ -15,6 +15,7 @@ import (
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/store"
"github.com/mattermost/platform/utils"
goi18n "github.com/nicksnyder/go-i18n/i18n"
)
var sessionCache *utils.Cache = utils.NewLru(model.SESSION_CACHE_SIZE)
@@ -29,6 +30,8 @@ type Context struct {
teamURL string
siteURL string
SessionTokenIndex int64
T goi18n.TranslateFunc
Locale string
}
type Page struct {
@@ -41,6 +44,7 @@ type Page struct {
Channel *model.Channel
PostID string
SessionTokenIndex int64
Locale string
}
func ApiAppHandler(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
@@ -81,10 +85,10 @@ type handler struct {
}
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
l4g.Debug("%v", r.URL.Path)
c := &Context{}
c.T, c.Locale = utils.GetTranslationsAndLocale(w, r)
c.RequestId = model.NewId()
c.IpAddress = GetIpAddress(r)
@@ -164,10 +168,10 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if session == nil || session.IsExpired() {
c.RemoveSessionCookie(w, r)
c.Err = model.NewAppError("ServeHTTP", "Invalid or expired session, please login again.", "token="+token)
c.Err = model.NewLocAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token)
c.Err.StatusCode = http.StatusUnauthorized
} else if !session.IsOAuth && isTokenFromQueryString {
c.Err = model.NewAppError("ServeHTTP", "Session is not OAuth but token was provided in the query string", "token="+token)
c.Err = model.NewLocAppError("ServeHTTP", "api.context.token_provided.app_error", nil, "token="+token)
c.Err.StatusCode = http.StatusUnauthorized
} else {
c.Session = *session
@@ -194,7 +198,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if c.Err == nil && h.isUserActivity && token != "" && len(c.Session.UserId) > 0 {
go func() {
if err := (<-Srv.Store.User().UpdateUserAndSessionActivity(c.Session.UserId, c.Session.Id, model.GetMillis())).Err; err != nil {
l4g.Error("Failed to update LastActivityAt for user_id=%v and session_id=%v, err=%v", c.Session.UserId, c.Session.Id, err)
l4g.Error(utils.T("api.context.last_activity_at.error"), c.Session.UserId, c.Session.Id, err)
}
}()
}
@@ -204,6 +208,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
if c.Err != nil {
c.Err.Translate(c.T)
c.Err.RequestId = c.RequestId
c.LogError(c.Err)
c.Err.Where = r.URL.Path
@@ -249,13 +254,13 @@ func (c *Context) LogAuditWithUserId(userId, extraInfo string) {
}
func (c *Context) LogError(err *model.AppError) {
l4g.Error("%v:%v code=%v rid=%v uid=%v ip=%v %v [details: %v]", c.Path, err.Where, err.StatusCode,
l4g.Error(utils.T("api.context.log.error"), c.Path, err.Where, err.StatusCode,
c.RequestId, c.Session.UserId, c.IpAddress, err.Message, err.DetailedError)
}
func (c *Context) UserRequired() {
if len(c.Session.UserId) == 0 {
c.Err = model.NewAppError("", "Invalid or expired session, please login again.", "UserRequired")
c.Err = model.NewLocAppError("", "api.context.session_expired.app_error", nil, "UserRequired")
c.Err.StatusCode = http.StatusUnauthorized
return
}
@@ -263,11 +268,11 @@ func (c *Context) UserRequired() {
func (c *Context) SystemAdminRequired() {
if len(c.Session.UserId) == 0 {
c.Err = model.NewAppError("", "Invalid or expired session, please login again.", "SystemAdminRequired")
c.Err = model.NewLocAppError("", "api.context.session_expired.app_error", nil, "SystemAdminRequired")
c.Err.StatusCode = http.StatusUnauthorized
return
} else if !c.IsSystemAdmin() {
c.Err = model.NewAppError("", "You do not have the appropriate permissions", "AdminRequired")
c.Err = model.NewLocAppError("", "api.context.permissions.app_error", nil, "AdminRequired")
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -285,7 +290,7 @@ func (c *Context) HasPermissionsToUser(userId string, where string) bool {
return true
}
c.Err = model.NewAppError(where, "You do not have the appropriate permissions", "userId="+userId)
c.Err = model.NewLocAppError(where, "api.context.permissions.app_error", nil, "userId="+userId)
c.Err.StatusCode = http.StatusForbidden
return false
}
@@ -300,7 +305,7 @@ func (c *Context) HasPermissionsToTeam(teamId string, where string) bool {
return true
}
c.Err = model.NewAppError(where, "You do not have the appropriate permissions", "userId="+c.Session.UserId+", teamId="+teamId)
c.Err = model.NewLocAppError(where, "api.context.permissions.app_error", nil, "userId="+c.Session.UserId+", teamId="+teamId)
c.Err.StatusCode = http.StatusForbidden
return false
}
@@ -310,7 +315,7 @@ func (c *Context) HasPermissionsToChannel(sc store.StoreChannel, where string) b
c.Err = cresult.Err
return false
} else if cresult.Data.(int64) != 1 {
c.Err = model.NewAppError(where, "You do not have the appropriate permissions", "userId="+c.Session.UserId)
c.Err = model.NewLocAppError(where, "api.context.permissions.app_error", nil, "userId="+c.Session.UserId)
c.Err.StatusCode = http.StatusForbidden
return false
}
@@ -323,7 +328,7 @@ func (c *Context) HasSystemAdminPermissions(where string) bool {
return true
}
c.Err = model.NewAppError(where, "You do not have the appropriate permissions (system)", "userId="+c.Session.UserId)
c.Err = model.NewLocAppError(where, "api.context.system_permissions.app_error", nil, "userId="+c.Session.UserId)
c.Err.StatusCode = http.StatusForbidden
return false
}
@@ -371,12 +376,12 @@ func (c *Context) RemoveSessionCookie(w http.ResponseWriter, r *http.Request) {
}
func (c *Context) SetInvalidParam(where string, name string) {
c.Err = model.NewAppError(where, "Invalid "+name+" parameter", "")
c.Err = model.NewLocAppError(where, "api.context.invalid_param.app_error", map[string]interface{}{"Name": name}, "")
c.Err.StatusCode = http.StatusBadRequest
}
func (c *Context) SetUnknownError(where string, details string) {
c.Err = model.NewAppError(where, "An unknown error has occured. Please contact support.", details)
c.Err = model.NewLocAppError(where, "api.context.unknown.app_error", nil, details)
}
func (c *Context) setTeamURL(url string, valid bool) {
@@ -402,7 +407,7 @@ func (c *Context) GetTeamURL() string {
if !c.teamURLValid {
c.SetTeamURLFromSession()
if !c.teamURLValid {
l4g.Debug("TeamURL accessed when not valid. Team URL should not be used in api functions or those that are team independent")
l4g.Debug(utils.T("api.context.invalid_team_url.debug"))
}
}
return c.teamURL
@@ -507,7 +512,7 @@ func RenderWebError(err *model.AppError, w http.ResponseWriter, r *http.Request)
}
func Handle404(w http.ResponseWriter, r *http.Request) {
err := model.NewAppError("Handle404", "Sorry, we could not find the page.", "")
err := model.NewLocAppError("Handle404", "api.context.404.app_error", nil, "")
err.StatusCode = http.StatusNotFound
l4g.Error("%v: code=404 ip=%v", r.URL.Path, GetIpAddress(r))
RenderWebError(err, w, r)
@@ -521,7 +526,7 @@ func GetSession(token string) *model.Session {
if session == nil {
if sessionResult := <-Srv.Store.Session().Get(token); sessionResult.Err != nil {
l4g.Error("Invalid session token=" + token + ", err=" + sessionResult.Err.DetailedError)
l4g.Error(utils.T("api.context.invalid_token.error"), token, sessionResult.Err.DetailedError)
} else {
session = sessionResult.Data.(*model.Session)

Просмотреть файл

@@ -70,10 +70,10 @@ func ExportToWriter(w io.Writer, options *ExportOptions) *model.AppError {
// Write our options to file
if optionsFile, err := zipWriter.Create(EXPORT_OPTIONS_FILE); err != nil {
return model.NewAppError("ExportToWriter", "Unable to create options file", err.Error())
return model.NewLocAppError("ExportToWriter", "api.export.options.create.app_error", nil, err.Error())
} else {
if _, err := optionsFile.Write([]byte(options.ToJson())); err != nil {
return model.NewAppError("ExportToWriter", "Unable to write to options file", err.Error())
return model.NewLocAppError("ExportToWriter", "api.export.options.write.app_error", nil, err.Error())
}
}
@@ -109,10 +109,10 @@ func ExportTeams(writer ExportWriter, options *ExportOptions) *model.AppError {
teams[i].PreExport()
if teamFile, err := writer.Create(EXPORT_TEAMS_FOLDER + "/" + teams[i].Name + ".json"); err != nil {
return model.NewAppError("ExportTeams", "Unable to open file for export", err.Error())
return model.NewLocAppError("ExportTeams", "api.export.open_file.app_error", nil, err.Error())
} else {
if _, err := teamFile.Write([]byte(teams[i].ToJson())); err != nil {
return model.NewAppError("ExportTeams", "Unable to write to team export file", err.Error())
return model.NewLocAppError("ExportTeams", "api.export.write_file.app_error", nil, err.Error())
}
}
@@ -162,10 +162,10 @@ func ExportChannels(writer ExportWriter, options *ExportOptions, teamId string)
channels[i].PreExport()
if channelFile, err := writer.Create(EXPORT_CHANNELS_FOLDER + "/" + channels[i].Id + ".json"); err != nil {
return model.NewAppError("ExportChannels", "Unable to open file for export", err.Error())
return model.NewLocAppError("ExportChannels", "api.export.open_file.app_error", nil, err.Error())
} else {
if _, err := channelFile.Write([]byte(channels[i].ToJson())); err != nil {
return model.NewAppError("ExportChannels", "Unable to write to export file", err.Error())
return model.NewLocAppError("ExportChannels", "api.export.write_file.app_error", nil, err.Error())
}
}
@@ -177,14 +177,14 @@ func ExportChannels(writer ExportWriter, options *ExportOptions, teamId string)
}
if membersFile, err := writer.Create(EXPORT_CHANNELS_FOLDER + "/" + channels[i].Id + "_members.json"); err != nil {
return model.NewAppError("ExportChannels", "Unable to open file for export", err.Error())
return model.NewLocAppError("ExportChannels", "api.export.open_file.app_error", nil, err.Error())
} else {
result, err2 := json.Marshal(members)
if err2 != nil {
return model.NewAppError("ExportChannels", "Unable to convert to json", err.Error())
return model.NewLocAppError("ExportChannels", "api.export.json.app_error", nil, err.Error())
}
if _, err3 := membersFile.Write([]byte(result)); err3 != nil {
return model.NewAppError("ExportChannels", "Unable to write to export file", err.Error())
return model.NewLocAppError("ExportChannels", "api.export.write_file.app_error", nil, err.Error())
}
}
}
@@ -209,14 +209,14 @@ func ExportPosts(writer ExportWriter, options *ExportOptions, channelId string)
// Export the posts
if postsFile, err := writer.Create(EXPORT_POSTS_FOLDER + "/" + channelId + "_posts.json"); err != nil {
return model.NewAppError("ExportPosts", "Unable to open file for export", err.Error())
return model.NewLocAppError("ExportPosts", "api.export.open_file.app_error", nil, err.Error())
} else {
result, err2 := json.Marshal(posts)
if err2 != nil {
return model.NewAppError("ExportPosts", "Unable to convert to json", err.Error())
return model.NewLocAppError("ExportPosts", "api.export.json.app_error", nil, err.Error())
}
if _, err3 := postsFile.Write([]byte(result)); err3 != nil {
return model.NewAppError("ExportPosts", "Unable to write to export file", err.Error())
return model.NewLocAppError("ExportPosts", "api.export.write_file.app_error", nil, err.Error())
}
}
@@ -234,14 +234,14 @@ func ExportUsers(writer ExportWriter, options *ExportOptions, teamId string) *mo
// Write the users
if usersFile, err := writer.Create(EXPORT_USERS_FOLDER + "/" + teamId + "_users.json"); err != nil {
return model.NewAppError("ExportUsers", "Unable to open file for export", err.Error())
return model.NewLocAppError("ExportUsers", "api.export.open_file.app_error", nil, err.Error())
} else {
result, err2 := json.Marshal(users)
if err2 != nil {
return model.NewAppError("ExportUsers", "Unable to convert to json", err.Error())
return model.NewLocAppError("ExportUsers", "api.export.json.app_error", nil, err.Error())
}
if _, err3 := usersFile.Write([]byte(result)); err3 != nil {
return model.NewAppError("ExportUsers", "Unable to write to export file", err.Error())
return model.NewLocAppError("ExportUsers", "api.export.write_file.app_error", nil, err.Error())
}
}
return nil
@@ -250,12 +250,12 @@ func ExportUsers(writer ExportWriter, options *ExportOptions, teamId string) *mo
func copyDirToExportWriter(writer ExportWriter, inPath string, outPath string) *model.AppError {
dir, err := os.Open(inPath)
if err != nil {
return model.NewAppError("copyDirToExportWriter", "Unable to open directory", err.Error())
return model.NewLocAppError("copyDirToExportWriter", "api.export.open_dir.app_error", nil, err.Error())
}
fileInfoList, err := dir.Readdir(0)
if err != nil {
return model.NewAppError("copyDirToExportWriter", "Unable to read directory", err.Error())
return model.NewLocAppError("copyDirToExportWriter", "api.export.read_dir.app_error", nil, err.Error())
}
for _, fileInfo := range fileInfoList {
@@ -263,11 +263,11 @@ func copyDirToExportWriter(writer ExportWriter, inPath string, outPath string) *
copyDirToExportWriter(writer, inPath+"/"+fileInfo.Name(), outPath+"/"+fileInfo.Name())
} else {
if toFile, err := writer.Create(outPath + "/" + fileInfo.Name()); err != nil {
return model.NewAppError("copyDirToExportWriter", "Unable to open file for export", err.Error())
return model.NewLocAppError("copyDirToExportWriter", "api.export.open_file.app_error", nil, err.Error())
} else {
fromFile, err := os.Open(inPath + "/" + fileInfo.Name())
if err != nil {
return model.NewAppError("copyDirToExportWriter", "Unable to open file", err.Error())
return model.NewLocAppError("copyDirToExportWriter", "api.export.open.app_error", nil, err.Error())
}
io.Copy(toFile, fromFile)
}
@@ -281,7 +281,7 @@ func ExportLocalStorage(writer ExportWriter, options *ExportOptions, teamId stri
teamDir := utils.Cfg.FileSettings.Directory + "teams/" + teamId
if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_S3 {
return model.NewAppError("ExportLocalStorage", "S3 is not supported for local storage export.", "")
return model.NewLocAppError("ExportLocalStorage", "api.export.s3.app_error", nil, "")
} else if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_LOCAL {
if err := copyDirToExportWriter(writer, teamDir, EXPORT_LOCAL_STORAGE_FOLDER); err != nil {
return err

Просмотреть файл

@@ -58,7 +58,7 @@ const (
var fileInfoCache *utils.Cache = utils.NewLru(1000)
func InitFile(r *mux.Router) {
l4g.Debug("Initializing file api routes")
l4g.Debug(utils.T("api.file.init.debug"))
sr := r.PathPrefix("/files").Subrouter()
sr.Handle("/upload", ApiUserRequired(uploadFile)).Methods("POST")
@@ -70,13 +70,13 @@ func InitFile(r *mux.Router) {
func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
if len(utils.Cfg.FileSettings.DriverName) == 0 {
c.Err = model.NewAppError("uploadFile", "Unable to upload file. Image storage is not configured.", "")
c.Err = model.NewLocAppError("uploadFile", "api.file.upload_file.storage.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
if r.ContentLength > model.MAX_FILE_SIZE {
c.Err = model.NewAppError("uploadFile", "Unable to upload file. File is too large.", "")
c.Err = model.NewLocAppError("uploadFile", "api.file.upload_file.too_large.app_error", nil, "")
c.Err.StatusCode = http.StatusRequestEntityTooLarge
return
}
@@ -139,10 +139,10 @@ func uploadFile(c *Context, w http.ResponseWriter, r *http.Request) {
// Decode image config first to check dimensions before loading the whole thing into memory later on
config, _, err := image.DecodeConfig(bytes.NewReader(buf.Bytes()))
if err != nil {
c.Err = model.NewAppError("uploadFile", "Unable to upload image file.", err.Error())
c.Err = model.NewLocAppError("uploadFile", "api.file.upload_file.image.app_error", nil, err.Error())
return
} else if config.Width*config.Height > MaxImageSize {
c.Err = model.NewAppError("uploadFile", "Unable to upload image file. File is too large.", "File exceeds max image size.")
c.Err = model.NewLocAppError("uploadFile", "api.file.upload_file.large_image.app_error", nil, c.T("api.file.file_upload.exceeds"))
return
}
}
@@ -180,7 +180,7 @@ func handleImagesAndForget(filenames []string, fileData [][]byte, teamId, channe
// Decode image bytes into Image object
img, imgType, err := image.Decode(bytes.NewReader(fileData[i]))
if err != nil {
l4g.Error("Unable to decode image channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
l4g.Error(utils.T("api.file.handle_images_forget.decode.error"), channelId, userId, filename, err)
return
}
@@ -233,12 +233,12 @@ func handleImagesAndForget(filenames []string, fileData [][]byte, teamId, channe
buf := new(bytes.Buffer)
err = jpeg.Encode(buf, thumbnail, &jpeg.Options{Quality: 90})
if err != nil {
l4g.Error("Unable to encode image as jpeg channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
l4g.Error(utils.T("api.file.handle_images_forget.encode_jpeg.error"), channelId, userId, filename, err)
return
}
if err := writeFile(buf.Bytes(), dest+name+"_thumb.jpg"); err != nil {
l4g.Error("Unable to upload thumbnail channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
l4g.Error(utils.T("api.file.handle_images_forget.upload_thumb.error"), channelId, userId, filename, err)
return
}
}()
@@ -256,12 +256,12 @@ func handleImagesAndForget(filenames []string, fileData [][]byte, teamId, channe
err = jpeg.Encode(buf, preview, &jpeg.Options{Quality: 90})
if err != nil {
l4g.Error("Unable to encode image as preview jpg channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
l4g.Error(utils.T("api.file.handle_images_forget.encode_preview.error"), channelId, userId, filename, err)
return
}
if err := writeFile(buf.Bytes(), dest+name+"_preview.jpg"); err != nil {
l4g.Error("Unable to upload preview channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
l4g.Error(utils.T("api.file.handle_images_forget.upload_preview.error"), channelId, userId, filename, err)
return
}
}()
@@ -294,7 +294,7 @@ type ImageGetResult struct {
func getFileInfo(c *Context, w http.ResponseWriter, r *http.Request) {
if len(utils.Cfg.FileSettings.DriverName) == 0 {
c.Err = model.NewAppError("uploadFile", "Unable to get file info. Image storage is not configured.", "")
c.Err = model.NewLocAppError("uploadFile", "api.file.upload_file.storage.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
@@ -351,7 +351,7 @@ func getFileInfo(c *Context, w http.ResponseWriter, r *http.Request) {
func getFile(c *Context, w http.ResponseWriter, r *http.Request) {
if len(utils.Cfg.FileSettings.DriverName) == 0 {
c.Err = model.NewAppError("uploadFile", "Unable to get file. Image storage is not configured.", "")
c.Err = model.NewLocAppError("uploadFile", "api.file.upload_file.storage.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
@@ -379,6 +379,7 @@ func getFile(c *Context, w http.ResponseWriter, r *http.Request) {
hash := r.URL.Query().Get("h")
data := r.URL.Query().Get("d")
teamId := r.URL.Query().Get("t")
isDownload := r.URL.Query().Get("download") == "1"
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)
@@ -394,14 +395,14 @@ func getFile(c *Context, w http.ResponseWriter, r *http.Request) {
if len(hash) > 0 && len(data) > 0 && len(teamId) == 26 {
if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.FileSettings.PublicLinkSalt)) {
c.Err = model.NewAppError("getFile", "The public link does not appear to be valid", "")
c.Err = model.NewLocAppError("getFile", "api.file.get_file.public_invalid.app_error", nil, "")
return
}
props := model.MapFromJson(strings.NewReader(data))
t, err := strconv.ParseInt(props["time"], 10, 64)
if err != nil || model.GetMillis()-t > 1000*60*60*24*7 { // one week
c.Err = model.NewAppError("getFile", "The public link has expired", "")
c.Err = model.NewLocAppError("getFile", "api.file.get_file.public_expired.app_error", nil, "")
return
}
} else if !c.HasPermissionsToChannel(cchan, "getFile") {
@@ -411,7 +412,7 @@ func getFile(c *Context, w http.ResponseWriter, r *http.Request) {
f := <-fileData
if f == nil {
c.Err = model.NewAppError("getFile", "Could not find file.", "path="+path)
c.Err = model.NewLocAppError("getFile", "api.file.get_file.not_found.app_error", nil, "path="+path)
c.Err.StatusCode = http.StatusNotFound
return
}
@@ -420,16 +421,18 @@ func getFile(c *Context, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Length", strconv.Itoa(len(f)))
w.Header().Del("Content-Type") // Content-Type will be set automatically by the http writer
// attach extra headers to trigger a download on IE, Edge, and Safari
ua := user_agent.New(r.UserAgent())
bname, _ := ua.Browser()
if isDownload {
// attach extra headers to trigger a download on IE, Edge, and Safari
ua := user_agent.New(r.UserAgent())
bname, _ := ua.Browser()
if bname == "Edge" || bname == "Internet Explorer" || bname == "Safari" {
// trim off anything before the final / so we just get the file's name
parts := strings.Split(filename, "/")
if bname == "Edge" || bname == "Internet Explorer" || bname == "Safari" {
// trim off anything before the final / so we just get the file's name
parts := strings.Split(filename, "/")
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", "attachment;filename=\""+parts[len(parts)-1]+"\"")
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", "attachment;filename=\""+parts[len(parts)-1]+"\"")
}
}
w.Write(f)
@@ -449,13 +452,13 @@ func getFileAndForget(path string, fileData chan []byte) {
func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) {
if len(utils.Cfg.FileSettings.DriverName) == 0 {
c.Err = model.NewAppError("uploadFile", "Unable to get link. Image storage is not configured.", "")
c.Err = model.NewLocAppError("uploadFile", "api.file.upload_file.storage.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
if !utils.Cfg.FileSettings.EnablePublicLink {
c.Err = model.NewAppError("getPublicLink", "Public links have been disabled", "")
c.Err = model.NewLocAppError("getPublicLink", "api.file.get_public_link.disabled.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
}
@@ -500,13 +503,13 @@ func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) {
func getExport(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.HasPermissionsToTeam(c.Session.TeamId, "export") || !c.IsTeamAdmin() {
c.Err = model.NewAppError("getExport", "Only a team admin can retrieve exported data.", "userId="+c.Session.UserId)
c.Err = model.NewLocAppError("getExport", "api.file.get_export.team_admin.app_error", nil, "userId="+c.Session.UserId)
c.Err.StatusCode = http.StatusForbidden
return
}
data, err := readFile(EXPORT_PATH + EXPORT_FILENAME)
if err != nil {
c.Err = model.NewAppError("getExport", "Unable to retrieve exported file. Please re-export", err.Error())
c.Err = model.NewLocAppError("getExport", "api.file.get_export.retrieve.app_error", nil, err.Error())
return
}
@@ -538,14 +541,14 @@ func writeFile(f []byte, path string) *model.AppError {
}
if err != nil {
return model.NewAppError("writeFile", "Encountered an error writing to S3", err.Error())
return model.NewLocAppError("writeFile", "api.file.write_file.s3.app_error", nil, err.Error())
}
} else if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_LOCAL {
if err := writeFileLocally(f, utils.Cfg.FileSettings.Directory+path); err != nil {
return err
}
} else {
return model.NewAppError("writeFile", "File storage not configured properly. Please configure for either S3 or local server file storage.", "")
return model.NewLocAppError("writeFile", "api.file.write_file.configured.app_error", nil, "")
}
return nil
@@ -553,11 +556,11 @@ func writeFile(f []byte, path string) *model.AppError {
func writeFileLocally(f []byte, path string) *model.AppError {
if err := os.MkdirAll(filepath.Dir(path), 0774); err != nil {
return model.NewAppError("writeFile", "Encountered an error creating the directory for the new file", err.Error())
return model.NewLocAppError("writeFile", "api.file.write_file_locally.create_dir.app_error", nil, err.Error())
}
if err := ioutil.WriteFile(path, f, 0644); err != nil {
return model.NewAppError("writeFile", "Encountered an error writing to local server storage", err.Error())
return model.NewLocAppError("writeFile", "api.file.write_file_locally.writing.app_error", nil, err.Error())
}
return nil
@@ -583,31 +586,31 @@ func readFile(path string) ([]byte, *model.AppError) {
if f != nil {
return f, nil
} else if tries >= 3 {
return nil, model.NewAppError("readFile", "Unable to get file from S3", "path="+path+", err="+err.Error())
return nil, model.NewLocAppError("readFile", "api.file.read_file.get.app_error", nil, "path="+path+", err="+err.Error())
}
time.Sleep(3000 * time.Millisecond)
}
} else if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_LOCAL {
if f, err := ioutil.ReadFile(utils.Cfg.FileSettings.Directory + path); err != nil {
return nil, model.NewAppError("readFile", "Encountered an error reading from local server storage", err.Error())
return nil, model.NewLocAppError("readFile", "api.file.read_file.reading_local.app_error", nil, err.Error())
} else {
return f, nil
}
} else {
return nil, model.NewAppError("readFile", "File storage not configured properly. Please configure for either S3 or local server file storage.", "")
return nil, model.NewLocAppError("readFile", "api.file.read_file.configured.app_error", nil, "")
}
}
func openFileWriteStream(path string) (io.Writer, *model.AppError) {
if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_S3 {
return nil, model.NewAppError("openFileWriteStream", "S3 is not supported.", "")
return nil, model.NewLocAppError("openFileWriteStream", "api.file.open_file_write_stream.s3.app_error", nil, "")
} else if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_LOCAL {
if err := os.MkdirAll(filepath.Dir(utils.Cfg.FileSettings.Directory+path), 0774); err != nil {
return nil, model.NewAppError("openFileWriteStream", "Encountered an error creating the directory for the new file", err.Error())
return nil, model.NewLocAppError("openFileWriteStream", "api.file.open_file_write_stream.creating_dir.app_error", nil, err.Error())
}
if fileHandle, err := os.Create(utils.Cfg.FileSettings.Directory + path); err != nil {
return nil, model.NewAppError("openFileWriteStream", "Encountered an error writing to local server storage", err.Error())
return nil, model.NewLocAppError("openFileWriteStream", "api.file.open_file_write_stream.local_server.app_error", nil, err.Error())
} else {
fileHandle.Chmod(0644)
return fileHandle, nil
@@ -615,7 +618,7 @@ func openFileWriteStream(path string) (io.Writer, *model.AppError) {
}
return nil, model.NewAppError("openFileWriteStream", "File storage not configured properly. Please configure for either S3 or local server file storage.", "")
return nil, model.NewLocAppError("openFileWriteStream", "api.file.open_file_write_stream.configured.app_error", nil, "")
}
func closeFileWriteStream(file io.Writer) {

Просмотреть файл

@@ -6,6 +6,7 @@ package api
import (
l4g "github.com/alecthomas/log4go"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
)
//
@@ -17,7 +18,7 @@ func ImportPost(post *model.Post) {
post.Hashtags, _ = model.ParseHashtags(post.Message)
if result := <-Srv.Store.Post().Save(post); result.Err != nil {
l4g.Debug("Error saving post. user=" + post.UserId + ", message=" + post.Message)
l4g.Debug(utils.T("api.import.import_post.saving.debug"), post.UserId, post.Message)
}
}
@@ -25,17 +26,17 @@ func ImportUser(user *model.User) *model.User {
user.MakeNonNil()
if result := <-Srv.Store.User().Save(user); result.Err != nil {
l4g.Error("Error saving user. err=%v", result.Err)
l4g.Error(utils.T("api.import.import_user.saving.error"), result.Err)
return nil
} else {
ruser := result.Data.(*model.User)
if err := JoinDefaultChannels(ruser, ""); err != nil {
l4g.Error("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", ruser.Id, ruser.TeamId, err)
l4g.Error(utils.T("api.import.import_user.joining_default.error"), ruser.Id, ruser.TeamId, err)
}
if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil {
l4g.Error("Failed to set email verified err=%v", cresult.Err)
l4g.Error(utils.T("api.import.import_user.set_email.error"), cresult.Err)
}
return ruser

Просмотреть файл

@@ -15,7 +15,7 @@ import (
)
func InitLicense(r *mux.Router) {
l4g.Debug("Initializing license api routes")
l4g.Debug(utils.T("api.license.init.debug"))
sr := r.PathPrefix("/license").Subrouter()
sr.Handle("/add", ApiAdminSystemRequired(addLicense)).Methods("POST")
@@ -34,13 +34,13 @@ func addLicense(c *Context, w http.ResponseWriter, r *http.Request) {
fileArray, ok := m.File["license"]
if !ok {
c.Err = model.NewAppError("addLicense", "No file under 'license' in request", "")
c.Err = model.NewLocAppError("addLicense", "api.license.add_license.no_file.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}
if len(fileArray) <= 0 {
c.Err = model.NewAppError("addLicense", "Empty array under 'license' in request", "")
c.Err = model.NewLocAppError("addLicense", "api.license.add_license.array.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}
@@ -50,7 +50,7 @@ func addLicense(c *Context, w http.ResponseWriter, r *http.Request) {
file, err := fileData.Open()
defer file.Close()
if err != nil {
c.Err = model.NewAppError("addLicense", "Could not open license file", err.Error())
c.Err = model.NewLocAppError("addLicense", "api.license.add_license.open.app_error", nil, err.Error())
return
}
@@ -65,19 +65,19 @@ func addLicense(c *Context, w http.ResponseWriter, r *http.Request) {
if ok := utils.SetLicense(license); !ok {
c.LogAudit("failed - expired or non-started license")
c.Err = model.NewAppError("addLicense", "License is either expired or has not yet started.", "")
c.Err = model.NewLocAppError("addLicense", "api.license.add_license.expired.app_error", nil, "")
return
}
if err := writeFileLocally(data, utils.LicenseLocation()); err != nil {
c.LogAudit("failed - could not save license file")
c.Err = model.NewAppError("addLicense", "License did not save properly.", "path="+utils.LicenseLocation())
c.Err = model.NewLocAppError("addLicense", "api.license.add_license.save.app_error", nil, "path="+utils.LicenseLocation())
utils.RemoveLicense()
return
}
} else {
c.LogAudit("failed - invalid license")
c.Err = model.NewAppError("addLicense", "Invalid license file.", "")
c.Err = model.NewLocAppError("addLicense", "api.license.add_license.invalid.app_error", nil, "")
return
}
@@ -90,7 +90,7 @@ func removeLicense(c *Context, w http.ResponseWriter, r *http.Request) {
if ok := utils.RemoveLicense(); !ok {
c.LogAudit("failed - could not remove license file")
c.Err = model.NewAppError("removeLicense", "License did not remove properly.", "")
c.Err = model.NewLocAppError("removeLicense", "api.license.remove_license.remove.app_error", nil, "")
return
}

Просмотреть файл

@@ -14,7 +14,7 @@ import (
)
func InitOAuth(r *mux.Router) {
l4g.Debug("Initializing oauth api routes")
l4g.Debug(utils.T("api.oauth.init.debug"))
sr := r.PathPrefix("/oauth").Subrouter()
@@ -24,7 +24,7 @@ func InitOAuth(r *mux.Router) {
func registerOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
c.Err = model.NewAppError("registerOAuthApp", "The system admin has turned off OAuth service providing.", "")
c.Err = model.NewLocAppError("registerOAuthApp", "api.oauth.register_oauth_app.turn_off.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
@@ -58,7 +58,7 @@ func registerOAuthApp(c *Context, w http.ResponseWriter, r *http.Request) {
func allowOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
c.Err = model.NewAppError("allowOAuth", "The system admin has turned off OAuth service providing.", "")
c.Err = model.NewLocAppError("allowOAuth", "api.oauth.allow_oauth.turn_off.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
@@ -70,19 +70,19 @@ func allowOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
responseType := r.URL.Query().Get("response_type")
if len(responseType) == 0 {
c.Err = model.NewAppError("allowOAuth", "invalid_request: Bad response_type", "")
c.Err = model.NewLocAppError("allowOAuth", "api.oauth.allow_oauth.bad_response.app_error", nil, "")
return
}
clientId := r.URL.Query().Get("client_id")
if len(clientId) != 26 {
c.Err = model.NewAppError("allowOAuth", "invalid_request: Bad client_id", "")
c.Err = model.NewLocAppError("allowOAuth", "api.oauth.allow_oauth.bad_client.app_error", nil, "")
return
}
redirectUri := r.URL.Query().Get("redirect_uri")
if len(redirectUri) == 0 {
c.Err = model.NewAppError("allowOAuth", "invalid_request: Missing or bad redirect_uri", "")
c.Err = model.NewLocAppError("allowOAuth", "api.oauth.allow_oauth.bad_redirect.app_error", nil, "")
return
}
@@ -91,7 +91,7 @@ func allowOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
var app *model.OAuthApp
if result := <-Srv.Store.OAuth().GetApp(clientId); result.Err != nil {
c.Err = model.NewAppError("allowOAuth", "server_error: Error accessing the database", "")
c.Err = model.NewLocAppError("allowOAuth", "api.oauth.allow_oauth.database.app_error", nil, "")
return
} else {
app = result.Data.(*model.OAuthApp)
@@ -99,7 +99,7 @@ func allowOAuth(c *Context, w http.ResponseWriter, r *http.Request) {
if !app.IsValidRedirectURL(redirectUri) {
c.LogAudit("fail - redirect_uri did not match registered callback")
c.Err = model.NewAppError("allowOAuth", "invalid_request: Supplied redirect_uri did not match registered callback_url", "")
c.Err = model.NewLocAppError("allowOAuth", "api.oauth.allow_oauth.redirect_callback.app_error", nil, "")
return
}
@@ -132,7 +132,7 @@ func RevokeAccessToken(token string) *model.AppError {
var accessData *model.AccessData
if result := <-Srv.Store.OAuth().GetAccessData(token); result.Err != nil {
return model.NewAppError("RevokeAccessToken", "Error getting access token from DB before deletion", "")
return model.NewLocAppError("RevokeAccessToken", "api.oauth.revoke_access_token.get.app_error", nil, "")
} else {
accessData = result.Data.(*model.AccessData)
}
@@ -141,15 +141,15 @@ func RevokeAccessToken(token string) *model.AppError {
cchan := Srv.Store.OAuth().RemoveAuthData(accessData.AuthCode)
if result := <-tchan; result.Err != nil {
return model.NewAppError("RevokeAccessToken", "Error deleting access token from DB", "")
return model.NewLocAppError("RevokeAccessToken", "api.oauth.revoke_access_token.del_token.app_error", nil, "")
}
if result := <-cchan; result.Err != nil {
return model.NewAppError("RevokeAccessToken", "Error deleting authorization code from DB", "")
return model.NewLocAppError("RevokeAccessToken", "api.oauth.revoke_access_token.del_code.app_error", nil, "")
}
if result := <-schan; result.Err != nil {
return model.NewAppError("RevokeAccessToken", "Error deleting session from DB", "")
return model.NewLocAppError("RevokeAccessToken", "api.oauth.revoke_access_token.del_session.app_error", nil, "")
}
return nil
@@ -157,7 +157,7 @@ func RevokeAccessToken(token string) *model.AppError {
func GetAuthData(code string) *model.AuthData {
if result := <-Srv.Store.OAuth().GetAuthData(code); result.Err != nil {
l4g.Error("Couldn't find auth code for code=%s", code)
l4g.Error(utils.T("api.oauth.get_auth_data.find.error"), code)
return nil
} else {
return result.Data.(*model.AuthData)

Просмотреть файл

@@ -20,7 +20,7 @@ import (
)
func InitPost(r *mux.Router) {
l4g.Debug("Initializing post api routes")
l4g.Debug(utils.T("api.post.init.debug"))
r.Handle("/posts/search", ApiUserRequired(searchPosts)).Methods("GET")
r.Handle("/posts/{post_id}", ApiUserRequired(getPostById)).Methods("GET")
@@ -53,14 +53,16 @@ func createPost(c *Context, w http.ResponseWriter, r *http.Request) {
if rp, err := CreatePost(c, post, true); err != nil {
c.Err = err
if strings.Contains(c.Err.Message, "parameter") {
if c.Err.Id == "api.post.create_post.root_id.app_error" ||
c.Err.Id == "api.post.create_post.channel_root_id.app_error" ||
c.Err.Id == "api.post.create_post.parent_id.app_error" {
c.Err.StatusCode = http.StatusBadRequest
}
return
} else {
if result := <-Srv.Store.Channel().UpdateLastViewedAt(post.ChannelId, c.Session.UserId); result.Err != nil {
l4g.Error("Encountered error updating last viewed, channel_id=%s, user_id=%s, err=%v", post.ChannelId, c.Session.UserId, result.Err)
l4g.Error(utils.T("api.post.create_post.last_viewed.error"), post.ChannelId, c.Session.UserId, result.Err)
}
w.Write([]byte(rp.ToJson()))
@@ -76,11 +78,11 @@ func CreatePost(c *Context, post *model.Post, triggerWebhooks bool) (*model.Post
// Verify the parent/child relationships are correct
if pchan != nil {
if presult := <-pchan; presult.Err != nil {
return nil, model.NewAppError("createPost", "Invalid RootId parameter", "")
return nil, model.NewLocAppError("createPost", "api.post.create_post.root_id.app_error", nil, "")
} else {
list := presult.Data.(*model.PostList)
if len(list.Posts) == 0 || !list.IsChannelId(post.ChannelId) {
return nil, model.NewAppError("createPost", "Invalid ChannelId for RootId parameter", "")
return nil, model.NewLocAppError("createPost", "api.post.create_post.channel_root_id.app_error", nil, "")
}
if post.ParentId == "" {
@@ -90,7 +92,7 @@ func CreatePost(c *Context, post *model.Post, triggerWebhooks bool) (*model.Post
if post.RootId != post.ParentId {
parent := list.Posts[post.ParentId]
if parent == nil {
return nil, model.NewAppError("createPost", "Invalid ParentId parameter", "")
return nil, model.NewLocAppError("createPost", "api.post.create_post.parent_id.app_error", nil, "")
}
}
}
@@ -129,7 +131,7 @@ func CreatePost(c *Context, post *model.Post, triggerWebhooks bool) (*model.Post
doRemove = true
}
if doRemove {
l4g.Error("Bad filename discarded, filename=%v", path)
l4g.Error(utils.T("api.post.create_post.bad_filename.error"), path)
post.Filenames = append(post.Filenames[:i], post.Filenames[i+1:]...)
}
}
@@ -185,6 +187,28 @@ func CreateWebhookPost(c *Context, channelId, text, overrideUsername, overrideIc
attachment["text"] = aText
list[i] = attachment
}
if _, ok := attachment["pretext"]; ok {
aText := attachment["pretext"].(string)
aText = linkWithTextRegex.ReplaceAllString(aText, "[${2}](${1})")
attachment["pretext"] = aText
list[i] = attachment
}
if fVal, ok := attachment["fields"]; ok {
if fields, ok := fVal.([]interface{}); ok {
// parse attachment field links into Markdown format
for j, fInt := range fields {
field := fInt.(map[string]interface{})
if _, ok := field["text"]; ok {
fText := field["text"].(string)
fText = linkWithTextRegex.ReplaceAllString(fText, "[${2}](${1})")
field["text"] = fText
fields[j] = field
}
}
attachment["fields"] = fields
list[i] = attachment
}
}
}
post.AddProp(key, list)
}
@@ -195,7 +219,7 @@ func CreateWebhookPost(c *Context, channelId, text, overrideUsername, overrideIc
}
if _, err := CreatePost(c, post, false); err != nil {
return nil, model.NewAppError("CreateWebhookPost", "Error creating post", "err="+err.Message)
return nil, model.NewLocAppError("CreateWebhookPost", "api.post.create_webhook_post.creating.app_error", nil, "err="+err.Message)
}
return post, nil
@@ -209,7 +233,7 @@ func handlePostEventsAndForget(c *Context, post *model.Post, triggerWebhooks boo
var team *model.Team
if result := <-tchan; result.Err != nil {
l4g.Error("Encountered error getting team, team_id=%s, err=%v", c.Session.TeamId, result.Err)
l4g.Error(utils.T("api.post.handle_post_events_and_forget.team.error"), c.Session.TeamId, result.Err)
return
} else {
team = result.Data.(*model.Team)
@@ -217,7 +241,7 @@ func handlePostEventsAndForget(c *Context, post *model.Post, triggerWebhooks boo
var channel *model.Channel
if result := <-cchan; result.Err != nil {
l4g.Error("Encountered error getting channel, channel_id=%s, err=%v", post.ChannelId, result.Err)
l4g.Error(utils.T("api.post.handle_post_events_and_forget.channel.error"), post.ChannelId, result.Err)
return
} else {
channel = result.Data.(*model.Channel)
@@ -227,7 +251,7 @@ func handlePostEventsAndForget(c *Context, post *model.Post, triggerWebhooks boo
var user *model.User
if result := <-uchan; result.Err != nil {
l4g.Error("Encountered error getting user, user_id=%s, err=%v", post.UserId, result.Err)
l4g.Error(utils.T("api.post.handle_post_events_and_forget.user.error"), post.UserId, result.Err)
return
} else {
user = result.Data.(*model.User)
@@ -246,14 +270,14 @@ func handlePostEventsAndForget(c *Context, post *model.Post, triggerWebhooks boo
func makeDirectChannelVisible(teamId string, channelId string) {
var members []model.ChannelMember
if result := <-Srv.Store.Channel().GetMembers(channelId); result.Err != nil {
l4g.Error("Failed to get channel members channel_id=%v err=%v", channelId, result.Err.Message)
l4g.Error(utils.T("api.post.make_direct_channel_visible.get_members.error"), channelId, result.Err.Message)
return
} else {
members = result.Data.([]model.ChannelMember)
}
if len(members) != 2 {
l4g.Error("Failed to get 2 members for a direct channel channel_id=%v", channelId)
l4g.Error(utils.T("api.post.make_direct_channel_visible.get_2_members.error"), channelId)
return
}
@@ -271,7 +295,7 @@ func makeDirectChannelVisible(teamId string, channelId string) {
}
if saveResult := <-Srv.Store.Preference().Save(&model.Preferences{*preference}); saveResult.Err != nil {
l4g.Error("Failed to save direct channel preference user_id=%v other_user_id=%v err=%v", member.UserId, otherUserId, saveResult.Err.Message)
l4g.Error(utils.T("api.post.make_direct_channel_visible.save_pref.error"), member.UserId, otherUserId, saveResult.Err.Message)
} else {
message := model.NewMessage(teamId, channelId, member.UserId, model.ACTION_PREFERENCE_CHANGED)
message.Add("preference", preference.ToJson())
@@ -286,7 +310,7 @@ func makeDirectChannelVisible(teamId string, channelId string) {
preference.Value = "true"
if updateResult := <-Srv.Store.Preference().Save(&model.Preferences{preference}); updateResult.Err != nil {
l4g.Error("Failed to update direct channel preference user_id=%v other_user_id=%v err=%v", member.UserId, otherUserId, updateResult.Err.Message)
l4g.Error(utils.T("api.post.make_direct_channel_visible.update_pref.error"), member.UserId, otherUserId, updateResult.Err.Message)
} else {
message := model.NewMessage(teamId, channelId, member.UserId, model.ACTION_PREFERENCE_CHANGED)
message.Add("preference", preference.ToJson())
@@ -313,7 +337,7 @@ func handleWebhookEventsAndForget(c *Context, post *model.Post, team *model.Team
hooks := []*model.OutgoingWebhook{}
if result := <-hchan; result.Err != nil {
l4g.Error("Encountered error getting webhooks by team, err=%v", result.Err)
l4g.Error(utils.T("api.post.handle_webhook_events_and_forget.getting.error"), result.Err)
return
} else {
hooks = result.Data.([]*model.OutgoingWebhook)
@@ -364,17 +388,17 @@ func handleWebhookEventsAndForget(c *Context, post *model.Post, team *model.Team
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "application/json")
if resp, err := client.Do(req); err != nil {
l4g.Error("Event POST failed, err=%s", err.Error())
l4g.Error(utils.T("api.post.handle_webhook_events_and_forget.event_post.error"), err.Error())
} else {
respProps := model.MapFromJson(resp.Body)
// copy the context and create a mock session for posting the message
mockSession := model.Session{UserId: hook.CreatorId, TeamId: hook.TeamId, IsOAuth: false}
newContext := &Context{mockSession, model.NewId(), "", c.Path, nil, c.teamURLValid, c.teamURL, c.siteURL, 0}
newContext := &Context{mockSession, model.NewId(), "", c.Path, nil, c.teamURLValid, c.teamURL, c.siteURL, 0, c.T, c.Locale}
if text, ok := respProps["text"]; ok {
if _, err := CreateWebhookPost(newContext, post.ChannelId, text, respProps["username"], respProps["icon_url"], post.Props, post.Type); err != nil {
l4g.Error("Failed to create response post, err=%v", err)
l4g.Error(utils.T("api.post.handle_webhook_events_and_forget.create_post.error"), err)
}
}
}
@@ -398,25 +422,17 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
var channelName string
var bodyText string
var subjectText string
if channel.Type == model.CHANNEL_DIRECT {
bodyText = "You have one new message."
subjectText = "New Direct Message"
} else {
bodyText = "You have one new mention."
subjectText = "New Mention"
channelName = channel.DisplayName
}
var mentionedUsers []string
if result := <-uchan; result.Err != nil {
l4g.Error("Failed to retrieve user profiles team_id=%v, err=%v", c.Session.TeamId, result.Err)
l4g.Error(utils.T("api.post.send_notifications_and_forget.retrive_profiles.error"), c.Session.TeamId, result.Err)
return
} else {
profileMap := result.Data.(map[string]*model.User)
if _, ok := profileMap[post.UserId]; !ok {
l4g.Error("Post user_id not returned by GetProfiles user_id=%v", post.UserId)
l4g.Error(utils.T("api.post.send_notifications_and_forget.user_id.error"), post.UserId)
return
}
senderName := profileMap[post.UserId].Username
@@ -447,7 +463,7 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
// Find out who is a member of the channel, only keep those profiles
if eResult := <-echan; eResult.Err != nil {
l4g.Error("Failed to get channel members channel_id=%v err=%v", post.ChannelId, eResult.Err.Message)
l4g.Error(utils.T("api.post.send_notifications_and_forget.members.error"), post.ChannelId, eResult.Err.Message)
return
} else {
tempProfileMap := make(map[string]*model.User)
@@ -555,14 +571,6 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
// Build and send the emails
tm := time.Unix(post.CreateAt/1000, 0)
subjectPage := NewServerTemplatePage("post_subject")
subjectPage.Props["SiteURL"] = c.GetSiteURL()
subjectPage.Props["TeamDisplayName"] = team.DisplayName
subjectPage.Props["SubjectText"] = subjectText
subjectPage.Props["Month"] = tm.Month().String()[:3]
subjectPage.Props["Day"] = fmt.Sprintf("%d", tm.Day())
subjectPage.Props["Year"] = fmt.Sprintf("%d", tm.Year())
for id, doSend := range toEmailMap {
if !doSend {
@@ -574,6 +582,25 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
continue
}
userLocale := utils.GetUserTranslations(profileMap[id].Locale)
if channel.Type == model.CHANNEL_DIRECT {
bodyText = userLocale("api.post.send_notifications_and_forget.message_body")
subjectText = userLocale("api.post.send_notifications_and_forget.message_subject")
} else {
bodyText = userLocale("api.post.send_notifications_and_forget.mention_body")
subjectText = userLocale("api.post.send_notifications_and_forget.mention_subject")
channelName = channel.DisplayName
}
subjectPage := NewServerTemplatePage("post_subject")
subjectPage.Props["SiteURL"] = c.GetSiteURL()
subjectPage.Props["TeamDisplayName"] = team.DisplayName
subjectPage.Props["SubjectText"] = subjectText
subjectPage.Props["Month"] = tm.Month().String()[:3]
subjectPage.Props["Day"] = fmt.Sprintf("%d", tm.Day())
subjectPage.Props["Year"] = fmt.Sprintf("%d", tm.Year())
bodyPage := NewServerTemplatePage("post_body")
bodyPage.Props["SiteURL"] = c.GetSiteURL()
bodyPage.Props["Nickname"] = profileMap[id].FirstName
@@ -616,17 +643,18 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
attachmentPrefix += "s"
}
bodyPage.Props["PostMessage"] = fmt.Sprintf("%s: %s sent", attachmentPrefix, filenamesString)
bodyPage.Props["PostMessage"] = userLocale("api.post.send_notifications_and_forget.sent",
map[string]interface{}{"Prefix": attachmentPrefix, "Filenames": filenamesString})
}
if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err)
l4g.Error(utils.T("api.post.send_notifications_and_forget.send.error"), profileMap[id].Email, err)
}
if *utils.Cfg.EmailSettings.SendPushNotifications {
sessionChan := Srv.Store.Session().GetSessions(id)
if result := <-sessionChan; result.Err != nil {
l4g.Error("Failed to retrieve sessions in notifications id=%v, err=%v", id, result.Err)
l4g.Error(utils.T("api.post.send_notifications_and_forget.sessions.error"), id, result.Err)
} else {
sessions := result.Data.([]*model.Session)
alreadySeen := make(map[string]string)
@@ -649,17 +677,17 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
}
if channel.Type == model.CHANNEL_DIRECT {
msg.Message = senderName + " sent you a direct message"
msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_message")
} else {
msg.Message = senderName + " mentioned you in " + channelName
msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_mention") + channelName
}
httpClient := http.Client{}
request, _ := http.NewRequest("POST", *utils.Cfg.EmailSettings.PushNotificationServer+"/api/v1/send_push", strings.NewReader(msg.ToJson()))
l4g.Debug("Sending push notification to " + msg.DeviceId + " with msg of '" + msg.Message + "'")
l4g.Debug(utils.T("api.post.send_notifications_and_forget.push_notification.debug"), msg.DeviceId, msg.Message)
if _, err := httpClient.Do(request); err != nil {
l4g.Error("Failed to send push notificationid=%v, err=%v", id, err)
l4g.Error(utils.T("api.post.send_notifications_and_forget.push_notification.error"), id, err)
}
}
}
@@ -695,7 +723,7 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team,
func updateMentionCountAndForget(channelId, userId string) {
go func() {
if result := <-Srv.Store.Channel().IncrementMentionCount(channelId, userId); result.Err != nil {
l4g.Error("Failed to update mention count for user_id=%v on channel_id=%v err=%v", userId, channelId, result.Err)
l4g.Error(utils.T("api.post.update_mention_count_and_forget.update_error"), userId, channelId, result.Err)
}
}()
}
@@ -723,19 +751,20 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) {
oldPost = result.Data.(*model.PostList).Posts[post.Id]
if oldPost == nil {
c.Err = model.NewAppError("updatePost", "We couldn't find the existing post or comment to update.", "id="+post.Id)
c.Err = model.NewLocAppError("updatePost", "api.post.update_post.find.app_error", nil, "id="+post.Id)
c.Err.StatusCode = http.StatusBadRequest
return
}
if oldPost.UserId != c.Session.UserId {
c.Err = model.NewAppError("updatePost", "You do not have the appropriate permissions", "oldUserId="+oldPost.UserId)
c.Err = model.NewLocAppError("updatePost", "api.post.update_post.permissions.app_error", nil, "oldUserId="+oldPost.UserId)
c.Err.StatusCode = http.StatusForbidden
return
}
if oldPost.DeleteAt != 0 {
c.Err = model.NewAppError("updatePost", "You do not have the appropriate permissions", "Already delted id="+post.Id)
c.Err = model.NewLocAppError("updatePost", "api.post.update_post.permissions.app_error", nil,
c.T("api.post.update_post.permissions_details.app_error", map[string]interface{}{"PostId": post.Id}))
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -870,7 +899,7 @@ func getPost(c *Context, w http.ResponseWriter, r *http.Request) {
list := result.Data.(*model.PostList)
if !list.IsChannelId(channelId) {
c.Err = model.NewAppError("getPost", "You do not have the appropriate permissions", "")
c.Err = model.NewLocAppError("getPost", "api.post.get_post.permissions.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -896,7 +925,7 @@ func getPostById(c *Context, w http.ResponseWriter, r *http.Request) {
list := result.Data.(*model.PostList)
if len(list.Order) != 1 {
c.Err = model.NewAppError("getPostById", "Unable to get post", "")
c.Err = model.NewLocAppError("getPostById", "api.post_get_post_by_id.get.app_error", nil, "")
return
}
post := list.Posts[list.Order[0]]
@@ -950,13 +979,13 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
}
if post.ChannelId != channelId {
c.Err = model.NewAppError("deletePost", "You do not have the appropriate permissions", "")
c.Err = model.NewLocAppError("deletePost", "api.post.delete_post.permissions.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
if post.UserId != c.Session.UserId && !c.IsTeamAdmin() {
c.Err = model.NewAppError("deletePost", "You do not have the appropriate permissions", "")
c.Err = model.NewLocAppError("deletePost", "api.post.delete_post.permissions.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -980,9 +1009,11 @@ func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
func getPostsBefore(c *Context, w http.ResponseWriter, r *http.Request) {
getPostsBeforeOrAfter(c, w, r, true)
}
func getPostsAfter(c *Context, w http.ResponseWriter, r *http.Request) {
getPostsBeforeOrAfter(c, w, r, false)
}
func getPostsBeforeOrAfter(c *Context, w http.ResponseWriter, r *http.Request, before bool) {
params := mux.Vars(r)

Просмотреть файл

@@ -7,11 +7,12 @@ import (
l4g "github.com/alecthomas/log4go"
"github.com/gorilla/mux"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
"net/http"
)
func InitPreference(r *mux.Router) {
l4g.Debug("Initializing preference api routes")
l4g.Debug(utils.T("api.preference.init.debug"))
sr := r.PathPrefix("/preferences").Subrouter()
sr.Handle("/", ApiUserRequired(getAllPreferences)).Methods("GET")
@@ -33,14 +34,16 @@ func getAllPreferences(c *Context, w http.ResponseWriter, r *http.Request) {
func savePreferences(c *Context, w http.ResponseWriter, r *http.Request) {
preferences, err := model.PreferencesFromJson(r.Body)
if err != nil {
c.Err = model.NewAppError("savePreferences", "Unable to decode preferences from request", err.Error())
c.Err = model.NewLocAppError("savePreferences", "api.preference.save_preferences.decode.app_error", nil, err.Error())
c.Err.StatusCode = http.StatusBadRequest
return
}
for _, preference := range preferences {
if c.Session.UserId != preference.UserId {
c.Err = model.NewAppError("savePreferences", "Unable to set preferences for other user", "session.user_id="+c.Session.UserId+", preference.user_id="+preference.UserId)
c.Err = model.NewLocAppError("savePreferences", "api.preference.save_preferences.set.app_error", nil,
c.T("api.preference.save_preferences.set_details.app_error",
map[string]interface{}{"SessionUserId": c.Session.UserId, "PreferenceUserId": preference.UserId}))
c.Err.StatusCode = http.StatusUnauthorized
return
}

Просмотреть файл

@@ -25,7 +25,7 @@ var Srv *Server
func NewServer() {
l4g.Info("Server is initializing...")
l4g.Info(utils.T("api.server.new_server.init.info"))
Srv = &Server{}
Srv.Store = store.NewSqlStore()
@@ -35,13 +35,13 @@ func NewServer() {
}
func StartServer() {
l4g.Info("Starting Server...")
l4g.Info("Server is listening on " + utils.Cfg.ServiceSettings.ListenAddress)
l4g.Info(utils.T("api.server.start_server.starting.info"))
l4g.Info(utils.T("api.server.start_server.listening.info"), utils.Cfg.ServiceSettings.ListenAddress)
var handler http.Handler = Srv.Router
if utils.Cfg.RateLimitSettings.EnableRateLimiter {
l4g.Info("RateLimiter is enabled")
l4g.Info(utils.T("api.server.start_server.rate.info"))
vary := throttled.VaryBy{}
@@ -53,7 +53,7 @@ func StartServer() {
vary.Headers = strings.Fields(utils.Cfg.RateLimitSettings.VaryByHeader)
if utils.Cfg.RateLimitSettings.VaryByRemoteAddr {
l4g.Warn("RateLimitSettings not configured properly using VaryByHeader and disabling VaryByRemoteAddr")
l4g.Warn(utils.T("api.server.start_server.rate.warn"))
vary.RemoteAddr = false
}
}
@@ -71,20 +71,20 @@ func StartServer() {
go func() {
err := manners.ListenAndServe(utils.Cfg.ServiceSettings.ListenAddress, handler)
if err != nil {
l4g.Critical("Error starting server, err:%v", err)
l4g.Critical(utils.T("api.server.start_server.starting.critical"), err)
time.Sleep(time.Second)
panic("Error starting server " + err.Error())
panic(utils.T("api.server.start_server.starting.panic") + err.Error())
}
}()
}
func StopServer() {
l4g.Info("Stopping Server...")
l4g.Info(utils.T("api.server.stop_server.stopping.info"))
manners.Close()
Srv.Store.Close()
hub.Stop()
l4g.Info("Server stopped")
l4g.Info(utils.T("api.server.stop_server.stopped.info"))
}

Просмотреть файл

@@ -9,6 +9,7 @@ import (
"encoding/json"
l4g "github.com/alecthomas/log4go"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
"io"
"mime/multipart"
"strconv"
@@ -44,7 +45,7 @@ func SlackConvertTimeStamp(ts string) int64 {
timeStamp, err := strconv.ParseInt(timeString, 10, 64)
if err != nil {
l4g.Warn("Bad timestamp detected")
l4g.Warn(utils.T("api.slackimport.slack_convert_timestamp.bad.warn"))
return 1
}
return timeStamp * 1000 // Convert to milliseconds
@@ -91,7 +92,7 @@ func SlackParsePosts(data io.Reader) []SlackPost {
func SlackAddUsers(teamId string, slackusers []SlackUser, log *bytes.Buffer) map[string]*model.User {
// Log header
log.WriteString("\r\n Users Created\r\n")
log.WriteString(utils.T("api.slackimport.slack_add_users.created"))
log.WriteString("===============\r\n\r\n")
addedUsers := make(map[string]*model.User)
@@ -118,9 +119,9 @@ func SlackAddUsers(teamId string, slackusers []SlackUser, log *bytes.Buffer) map
if mUser := ImportUser(&newUser); mUser != nil {
addedUsers[sUser.Id] = mUser
log.WriteString("Email, Password: " + newUser.Email + ", " + password + "\r\n")
log.WriteString(utils.T("api.slackimport.slack_add_users.email_pwd", map[string]interface{}{"Email": newUser.Email, "Password": password}))
} else {
log.WriteString("Unable to import user: " + sUser.Username + "\r\n")
log.WriteString(utils.T("api.slackimport.slack_add_users.unable_import", map[string]interface{}{"Username": sUser.Username}))
}
}
@@ -132,10 +133,10 @@ func SlackAddPosts(channel *model.Channel, posts []SlackPost, users map[string]*
switch {
case sPost.Type == "message" && (sPost.SubType == "" || sPost.SubType == "file_share"):
if sPost.User == "" {
l4g.Debug("Message without user")
l4g.Debug(utils.T("api.slackimport.slack_add_posts.without_user.debug"))
continue
} else if users[sPost.User] == nil {
l4g.Debug("User: " + sPost.User + " does not exist!")
l4g.Debug(utils.T("api.slackimport.slack_add_posts.user_no_exists.debug"), sPost.User)
continue
}
newPost := model.Post{
@@ -147,10 +148,10 @@ func SlackAddPosts(channel *model.Channel, posts []SlackPost, users map[string]*
ImportPost(&newPost)
case sPost.Type == "message" && sPost.SubType == "file_comment":
if sPost.Comment["user"] == "" {
l4g.Debug("Message without user")
l4g.Debug(utils.T("api.slackimport.slack_add_posts.msg_no_usr.debug"))
continue
} else if users[sPost.Comment["user"]] == nil {
l4g.Debug("User: " + sPost.User + " does not exist!")
l4g.Debug(utils.T("api.slackimport.slack_add_posts.user_no_exists.debug"), sPost.User)
continue
}
newPost := model.Post{
@@ -163,16 +164,16 @@ func SlackAddPosts(channel *model.Channel, posts []SlackPost, users map[string]*
case sPost.Type == "message" && sPost.SubType == "bot_message":
// In the future this will use the "Action Post" spec to post
// a message without using a username. For now we just warn that we don't handle this case
l4g.Warn("Slack bot posts are not imported yet")
l4g.Warn(utils.T("api.slackimport.slack_add_posts.bot.warn"))
default:
l4g.Warn("Unsupported post type: " + sPost.Type + ", " + sPost.SubType)
l4g.Warn(utils.T("api.slackimport.slack_add_posts.unsupported.warn"), sPost.Type, sPost.SubType)
}
}
}
func SlackAddChannels(teamId string, slackchannels []SlackChannel, posts map[string][]SlackPost, users map[string]*model.User, log *bytes.Buffer) map[string]*model.Channel {
// Write Header
log.WriteString("\r\n Channels Added \r\n")
log.WriteString(utils.T("api.slackimport.slack_add_channels.added"))
log.WriteString("=================\r\n\r\n")
addedChannels := make(map[string]*model.Channel)
@@ -188,12 +189,12 @@ func SlackAddChannels(teamId string, slackchannels []SlackChannel, posts map[str
if mChannel == nil {
// Maybe it already exists?
if result := <-Srv.Store.Channel().GetByName(teamId, sChannel.Name); result.Err != nil {
l4g.Debug("Failed to import: %s", newChannel.DisplayName)
log.WriteString("Failed to import: " + newChannel.DisplayName + "\r\n")
l4g.Debug(utils.T("api.slackimport.slack_add_channels.import_failed.debug"), newChannel.DisplayName)
log.WriteString(utils.T("api.slackimport.slack_add_channels.import_failed", map[string]interface{}{"DisplayName": newChannel.DisplayName}))
continue
} else {
mChannel = result.Data.(*model.Channel)
log.WriteString("Merged with existing channel: " + newChannel.DisplayName + "\r\n")
log.WriteString(utils.T("api.slackimport.slack_add_channels.merge", map[string]interface{}{"DisplayName": newChannel.DisplayName}))
}
}
log.WriteString(newChannel.DisplayName + "\r\n")
@@ -207,11 +208,11 @@ func SlackAddChannels(teamId string, slackchannels []SlackChannel, posts map[str
func SlackImport(fileData multipart.File, fileSize int64, teamID string) (*model.AppError, *bytes.Buffer) {
zipreader, err := zip.NewReader(fileData, fileSize)
if err != nil || zipreader.File == nil {
return model.NewAppError("SlackImport", "Unable to open zip file", err.Error()), nil
return model.NewLocAppError("SlackImport", "api.slackimport.slack_import.zip.app_error", nil, err.Error()), nil
}
// Create log file
log := bytes.NewBufferString("Mattermost Slack Import Log\r\n")
log := bytes.NewBufferString(utils.T("api.slackimport.slack_import.log"))
var channels []SlackChannel
var users []SlackUser
@@ -219,7 +220,7 @@ func SlackImport(fileData multipart.File, fileSize int64, teamID string) (*model
for _, file := range zipreader.File {
reader, err := file.Open()
if err != nil {
return model.NewAppError("SlackImport", "Unable to open: "+file.Name, err.Error()), log
return model.NewLocAppError("SlackImport", "api.slackimport.slack_import.open.app_error", map[string]interface{}{"Filename": file.Name}, err.Error()), log
}
if file.Name == "channels.json" {
channels = SlackParseChannels(reader)
@@ -243,11 +244,11 @@ func SlackImport(fileData multipart.File, fileSize int64, teamID string) (*model
addedUsers := SlackAddUsers(teamID, users, log)
SlackAddChannels(teamID, channels, posts, addedUsers, log)
log.WriteString("\r\n Notes \r\n")
log.WriteString(utils.T("api.slackimport.slack_import.notes"))
log.WriteString("=======\r\n\r\n")
log.WriteString("- Some posts may not have been imported because they where not supported by this importer.\r\n")
log.WriteString("- Slack bot posts are currently not supported.\r\n")
log.WriteString(utils.T("api.slackimport.slack_import.note1"))
log.WriteString(utils.T("api.slackimport.slack_import.note2"))
return nil, log
}

Просмотреть файл

@@ -19,7 +19,7 @@ import (
)
func InitTeam(r *mux.Router) {
l4g.Debug("Initializing team api routes")
l4g.Debug(utils.T("api.team.init.debug"))
sr := r.PathPrefix("/teams").Subrouter()
sr.Handle("/create", ApiAppHandler(createTeam)).Methods("POST")
@@ -40,7 +40,7 @@ func InitTeam(r *mux.Router) {
func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.Cfg.EmailSettings.EnableSignUpWithEmail {
c.Err = model.NewAppError("signupTeam", "Team sign-up with email is disabled.", "")
c.Err = model.NewLocAppError("signupTeam", "api.team.signup_team.email_disabled.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
@@ -147,7 +147,7 @@ func createTeamFromSSO(c *Context, w http.ResponseWriter, r *http.Request) {
func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.Cfg.EmailSettings.EnableSignUpWithEmail {
c.Err = model.NewAppError("createTeamFromSignup", "Team sign-up with email is disabled.", "")
c.Err = model.NewLocAppError("createTeamFromSignup", "api.team.create_team_from_signup.email_disabled.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
@@ -188,13 +188,13 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) {
teamSignup.User.Password = password
if !model.ComparePassword(teamSignup.Hash, fmt.Sprintf("%v:%v", teamSignup.Data, utils.Cfg.EmailSettings.InviteSalt)) {
c.Err = model.NewAppError("createTeamFromSignup", "The signup link does not appear to be valid", "")
c.Err = model.NewLocAppError("createTeamFromSignup", "api.team.create_team_from_signup.invalid_link.app_error", nil, "")
return
}
t, err := strconv.ParseInt(props["time"], 10, 64)
if err != nil || model.GetMillis()-t > 1000*60*60 { // one hour
c.Err = model.NewAppError("createTeamFromSignup", "The signup link has expired", "")
c.Err = model.NewLocAppError("createTeamFromSignup", "api.team.create_team_from_signup.expired_link.app_error", nil, "")
return
}
@@ -204,7 +204,7 @@ func createTeamFromSignup(c *Context, w http.ResponseWriter, r *http.Request) {
}
if found {
c.Err = model.NewAppError("createTeamFromSignup", "This URL is unavailable. Please try another.", "d="+teamSignup.Team.Name)
c.Err = model.NewLocAppError("createTeamFromSignup", "api.team.create_team_from_signup.unavailable.app_error", nil, "d="+teamSignup.Team.Name)
return
}
@@ -249,7 +249,7 @@ func createTeam(c *Context, w http.ResponseWriter, r *http.Request) {
func CreateTeam(c *Context, team *model.Team) *model.Team {
if !utils.Cfg.EmailSettings.EnableSignUpWithEmail {
c.Err = model.NewAppError("createTeam", "Team sign-up with email is disabled.", "")
c.Err = model.NewLocAppError("createTeam", "api.team.create_team.email_disabled.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return nil
}
@@ -283,7 +283,7 @@ func isTeamCreationAllowed(c *Context, email string) bool {
email = strings.ToLower(email)
if !utils.Cfg.TeamSettings.EnableTeamCreation {
c.Err = model.NewAppError("isTeamCreationAllowed", "Team creation has been disabled. Please ask your systems administrator for details.", "")
c.Err = model.NewLocAppError("isTeamCreationAllowed", "api.team.is_team_creation_allowed.disabled.app_error", nil, "")
return false
}
@@ -300,7 +300,7 @@ func isTeamCreationAllowed(c *Context, email string) bool {
}
if len(utils.Cfg.TeamSettings.RestrictCreationToDomains) > 0 && !matched {
c.Err = model.NewAppError("isTeamCreationAllowed", "Email must be from a specific domain (e.g. @example.com). Please ask your systems administrator for details.", "")
c.Err = model.NewLocAppError("isTeamCreationAllowed", "api.team.is_team_creation_allowed.domain.app_error", nil, "")
return false
}
@@ -445,7 +445,7 @@ func emailTeams(c *Context, w http.ResponseWriter, r *http.Request) {
bodyPage.Props = props
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("An error occured while sending an email in emailTeams err=%v", err)
l4g.Error(utils.T("api.team.email_teams.sending.error"), err)
}
w.Write([]byte(model.MapToJson(m)))
@@ -455,7 +455,7 @@ func emailTeams(c *Context, w http.ResponseWriter, r *http.Request) {
func inviteMembers(c *Context, w http.ResponseWriter, r *http.Request) {
invites := model.InvitesFromJson(r.Body)
if len(invites.Invites) == 0 {
c.Err = model.NewAppError("Team.InviteMembers", "No one to invite.", "")
c.Err = model.NewLocAppError("Team.InviteMembers", "api.team.invite_members.no_one.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}
@@ -483,7 +483,7 @@ func inviteMembers(c *Context, w http.ResponseWriter, r *http.Request) {
for i, invite := range invites.Invites {
if result := <-Srv.Store.User().GetByEmail(c.Session.TeamId, invite["email"]); result.Err == nil || result.Err.Message != store.MISSING_ACCOUNT_ERROR {
invNum = int64(i)
c.Err = model.NewAppError("invite_members", "This person is already on your team", strconv.FormatInt(invNum, 10))
c.Err = model.NewLocAppError("invite_members", "api.team.invite_members.already.app_error", nil, strconv.FormatInt(invNum, 10))
return
}
}
@@ -506,9 +506,9 @@ func InviteMembers(c *Context, team *model.Team, user *model.User, invites []str
senderRole := ""
if c.IsTeamAdmin() {
senderRole = "administrator"
senderRole = c.T("api.team.invite_members.admin")
} else {
senderRole = "member"
senderRole = c.T("api.team.invite_members.member")
}
subjectPage := NewServerTemplatePage("invite_subject")
@@ -532,11 +532,11 @@ func InviteMembers(c *Context, team *model.Team, user *model.User, invites []str
bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&h=%s", c.GetSiteURL(), url.QueryEscape(data), url.QueryEscape(hash))
if !utils.Cfg.EmailSettings.SendEmailNotifications {
l4g.Info("sending invitation to %v %v", invite, bodyPage.Props["Link"])
l4g.Info(utils.T("api.team.invite_members.sending.info"), invite, bodyPage.Props["Link"])
}
if err := utils.SendMail(invite, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("Failed to send invite email successfully err=%v", err)
l4g.Error(utils.T("api.team.invite_members.send.error"), err)
}
}
}
@@ -554,7 +554,7 @@ func updateTeam(c *Context, w http.ResponseWriter, r *http.Request) {
team.Id = c.Session.TeamId
if !c.IsTeamAdmin() {
c.Err = model.NewAppError("updateTeam", "You do not have the appropriate permissions", "userId="+c.Session.UserId)
c.Err = model.NewLocAppError("updateTeam", "api.team.update_team.permissions.app_error", nil, "userId="+c.Session.UserId)
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -586,7 +586,7 @@ func updateTeam(c *Context, w http.ResponseWriter, r *http.Request) {
}
func PermanentDeleteTeam(c *Context, team *model.Team) *model.AppError {
l4g.Warn("Attempting to permanently delete team %v id=%v", team.Name, team.Id)
l4g.Warn(utils.T("api.team.permanent_delete_team.attempting.warn"), team.Name, team.Id)
c.Path = "/teams/permanent_delete"
c.LogAuditWithUserId("", fmt.Sprintf("attempt teamId=%v", team.Id))
@@ -612,7 +612,7 @@ func PermanentDeleteTeam(c *Context, team *model.Team) *model.AppError {
return result.Err
}
l4g.Warn("Permanently deleted team %v id=%v", team.Name, team.Id)
l4g.Warn(utils.T("api.team.permanent_delete_team.deleted.warn"), team.Name, team.Id)
c.LogAuditWithUserId("", fmt.Sprintf("success teamId=%v", team.Id))
return nil
@@ -639,13 +639,13 @@ func getMyTeam(c *Context, w http.ResponseWriter, r *http.Request) {
func importTeam(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.HasPermissionsToTeam(c.Session.TeamId, "import") || !c.IsTeamAdmin() {
c.Err = model.NewAppError("importTeam", "Only a team admin can import data.", "userId="+c.Session.UserId)
c.Err = model.NewLocAppError("importTeam", "api.team.import_team.admin.app_error", nil, "userId="+c.Session.UserId)
c.Err.StatusCode = http.StatusForbidden
return
}
if err := r.ParseMultipartForm(10000000); err != nil {
c.Err = model.NewAppError("importTeam", "Could not parse multipart form", err.Error())
c.Err = model.NewLocAppError("importTeam", "api.team.import_team.parse.app_error", nil, err.Error())
return
}
@@ -654,27 +654,27 @@ func importTeam(c *Context, w http.ResponseWriter, r *http.Request) {
fileSizeStr, ok := r.MultipartForm.Value["filesize"]
if !ok {
c.Err = model.NewAppError("importTeam", "Filesize unavilable", "")
c.Err = model.NewLocAppError("importTeam", "api.team.import_team.unavailable.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}
fileSize, err := strconv.ParseInt(fileSizeStr[0], 10, 64)
if err != nil {
c.Err = model.NewAppError("importTeam", "Filesize not an integer", "")
c.Err = model.NewLocAppError("importTeam", "api.team.import_team.integer.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}
fileInfoArray, ok := r.MultipartForm.File["file"]
if !ok {
c.Err = model.NewAppError("importTeam", "No file under 'file' in request", "")
c.Err = model.NewLocAppError("importTeam", "api.team.import_team.no_file.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}
if len(fileInfoArray) <= 0 {
c.Err = model.NewAppError("importTeam", "Empty array under 'file' in request", "")
c.Err = model.NewLocAppError("importTeam", "api.team.import_team.array.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}
@@ -684,7 +684,7 @@ func importTeam(c *Context, w http.ResponseWriter, r *http.Request) {
fileData, err := fileInfo.Open()
defer fileData.Close()
if err != nil {
c.Err = model.NewAppError("importTeam", "Could not open file", err.Error())
c.Err = model.NewLocAppError("importTeam", "api.team.import_team.open.app_error", nil, err.Error())
c.Err.StatusCode = http.StatusBadRequest
return
}
@@ -706,7 +706,7 @@ func importTeam(c *Context, w http.ResponseWriter, r *http.Request) {
func exportTeam(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.HasPermissionsToTeam(c.Session.TeamId, "export") || !c.IsTeamAdmin() {
c.Err = model.NewAppError("exportTeam", "Only a team admin can export data.", "userId="+c.Session.UserId)
c.Err = model.NewLocAppError("exportTeam", "api.team.export_team.admin.app_error", nil, "userId="+c.Session.UserId)
c.Err.StatusCode = http.StatusForbidden
return
}

Просмотреть файл

@@ -11,8 +11,8 @@
<script src="/static/js/bootstrap-3.3.5.min.js"></script>
<script src="/static/js/react-bootstrap-0.25.1.min.js"></script>
<link id="favicon" rel="icon" href="/static/images/favicon.ico" type="image/x-icon">
<link rel="shortcut icon" href="/static/images/favicon.ico" type="image/x-icon">
<link id="favicon" rel="icon" href="/static/images/favicon/favicon-16x16.png" type="image/x-icon">
<link rel="shortcut icon" href="/static/images/favicon/favicon-16x16.png" type="image/x-icon">
<link href='/static/css/google-fonts.css' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="/static/css/styles.css">

Просмотреть файл

@@ -18,7 +18,7 @@
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
<h2 style="font-weight: normal; margin-top: 10px;">You updated your password</h2>
<p>You updated your password for {{.Props.TeamDisplayName}} on {{ .Props.TeamURL }} by {{.Props.Method}}.<br>If this change wasn't initiated by you, please contact your system administrator.</p>
<p>Your password has been updated for {{.Props.TeamDisplayName}} on {{ .Props.TeamURL }} by {{.Props.Method}}.<br>If this change wasn't initiated by you, please contact your system administrator.</p>
</td>
</tr>
<tr>

Просмотреть файл

@@ -1 +1 @@
{{define "password_change_subject"}}You updated your password for {{.Props.TeamDisplayName}} on {{ .ClientCfg.SiteName }}{{end}}
{{define "password_change_subject"}}Your password has been updated for {{.Props.TeamDisplayName}} on {{ .ClientCfg.SiteName }}{{end}}

Просмотреть файл

@@ -32,7 +32,7 @@ import (
)
func InitUser(r *mux.Router) {
l4g.Debug("Initializing user api routes")
l4g.Debug(utils.T("api.user.init.debug"))
sr := r.PathPrefix("/users").Subrouter()
sr.Handle("/create", ApiAppHandler(createUser)).Methods("POST")
@@ -64,7 +64,7 @@ func InitUser(r *mux.Router) {
func createUser(c *Context, w http.ResponseWriter, r *http.Request) {
if !utils.Cfg.EmailSettings.EnableSignUpWithEmail || !utils.Cfg.TeamSettings.EnableUserCreation {
c.Err = model.NewAppError("signupTeam", "User sign-up with email is disabled.", "")
c.Err = model.NewLocAppError("signupTeam", "api.user.create_user.signup_email_disabled.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
@@ -98,18 +98,18 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(strings.NewReader(data))
if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", data, utils.Cfg.EmailSettings.InviteSalt)) {
c.Err = model.NewAppError("createUser", "The signup link does not appear to be valid", "")
c.Err = model.NewLocAppError("createUser", "api.user.create_user.signup_link_invalid.app_error", nil, "")
return
}
t, err := strconv.ParseInt(props["time"], 10, 64)
if err != nil || model.GetMillis()-t > 1000*60*60*48 { // 48 hours
c.Err = model.NewAppError("createUser", "The signup link has expired", "")
c.Err = model.NewLocAppError("createUser", "api.user.create_user.signup_link_expired.app_error", nil, "")
return
}
if user.TeamId != props["id"] {
c.Err = model.NewAppError("createUser", "Invalid team name", data)
c.Err = model.NewLocAppError("createUser", "api.user.create_user.team_name.app_error", nil, data)
return
}
@@ -123,7 +123,7 @@ func createUser(c *Context, w http.ResponseWriter, r *http.Request) {
}
if !CheckUserDomain(user, utils.Cfg.TeamSettings.RestrictCreationToDomains) {
c.Err = model.NewAppError("createUser", "The email you provided does not belong to an accepted domain. Please contact your administrator or sign up with a different email.", "")
c.Err = model.NewLocAppError("createUser", "api.user.create_user.accepted_domain.app_error", nil, "")
return
}
@@ -208,27 +208,27 @@ func CreateUser(team *model.Team, user *model.User) (*model.User, *model.AppErro
user.MakeNonNil()
if result := <-Srv.Store.User().Save(user); result.Err != nil {
l4g.Error("Couldn't save the user err=%v", result.Err)
l4g.Error(utils.T("api.user.create_user.save.error"), result.Err)
return nil, result.Err
} else {
ruser := result.Data.(*model.User)
// Soft error if there is an issue joining the default channels
if err := JoinDefaultChannels(ruser, channelRole); err != nil {
l4g.Error("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", ruser.Id, ruser.TeamId, err)
l4g.Error(utils.T("api.user.create_user.joining.error"), ruser.Id, ruser.TeamId, err)
}
addDirectChannelsAndForget(ruser)
if user.EmailVerified {
if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil {
l4g.Error("Failed to set email verified err=%v", cresult.Err)
l4g.Error(utils.T("api.user.create_user.verified.error"), cresult.Err)
}
}
pref := model.Preference{UserId: ruser.Id, Category: model.PREFERENCE_CATEGORY_TUTORIAL_STEPS, Name: ruser.Id, Value: "0"}
if presult := <-Srv.Store.Preference().Save(&model.Preferences{pref}); presult.Err != nil {
l4g.Error("Encountered error saving tutorial preference, err=%v", presult.Err.Message)
l4g.Error(utils.T("api.user.create_user.tutorial.error"), presult.Err.Message)
}
ruser.Sanitize(map[string]bool{})
@@ -246,14 +246,14 @@ func CreateOAuthUser(c *Context, w http.ResponseWriter, r *http.Request, service
var user *model.User
provider := einterfaces.GetOauthProvider(service)
if provider == nil {
c.Err = model.NewAppError("CreateOAuthUser", service+" oauth not avlailable on this server", "")
c.Err = model.NewLocAppError("CreateOAuthUser", "api.user.create_oauth_user.not_available.app_error", map[string]interface{}{"Service": service}, "")
return nil
} else {
user = provider.GetUserFromJson(userData)
}
if user == nil {
c.Err = model.NewAppError("CreateOAuthUser", "Could not create user out of "+service+" user object", "")
c.Err = model.NewLocAppError("CreateOAuthUser", "api.user.create_oauth_user.create.app_error", map[string]interface{}{"Service": service}, "")
return nil
}
@@ -280,12 +280,14 @@ func CreateOAuthUser(c *Context, w http.ResponseWriter, r *http.Request, service
}
if result := <-suchan; result.Err == nil {
c.Err = model.NewAppError("signupCompleteOAuth", "This "+service+" account has already been used to sign up for team "+team.DisplayName, "email="+user.Email)
c.Err = model.NewLocAppError("signupCompleteOAuth", "api.user.create_oauth_user.already_used.app_error",
map[string]interface{}{"Service": service, "DisplayName": team.DisplayName}, "email="+user.Email)
return nil
}
if result := <-euchan; result.Err == nil {
c.Err = model.NewAppError("signupCompleteOAuth", "Team "+team.DisplayName+" already has a user with the email address attached to your "+service+" account", "email="+user.Email)
c.Err = model.NewLocAppError("signupCompleteOAuth", "api.user.create_oauth_user.already_attached.app_error",
map[string]interface{}{"Service": service, "DisplayName": team.DisplayName}, "email="+user.Email)
return nil
}
@@ -321,7 +323,7 @@ func sendWelcomeEmailAndForget(userId, email, teamName, teamDisplayName, siteURL
}
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("Failed to send welcome email successfully err=%v", err)
l4g.Error(utils.T("api.user.send_welcome_email_and_forget.failed.error"), err)
}
}()
}
@@ -330,7 +332,7 @@ func addDirectChannelsAndForget(user *model.User) {
go func() {
var profiles map[string]*model.User
if result := <-Srv.Store.User().GetProfiles(user.TeamId); result.Err != nil {
l4g.Error("Failed to add direct channel preferences for user user_id=%s, team_id=%s, err=%v", user.Id, user.TeamId, result.Err.Error())
l4g.Error(utils.T("api.user.add_direct_channels_and_forget.failed.error"), user.Id, user.TeamId, result.Err.Error())
return
} else {
profiles = result.Data.(map[string]*model.User)
@@ -360,7 +362,7 @@ func addDirectChannelsAndForget(user *model.User) {
}
if result := <-Srv.Store.Preference().Save(&preferences); result.Err != nil {
l4g.Error("Failed to add direct channel preferences for new user user_id=%s, eam_id=%s, err=%v", user.Id, user.TeamId, result.Err.Error())
l4g.Error(utils.T("api.user.add_direct_channels_and_forget.failed.error"), user.Id, user.TeamId, result.Err.Error())
}
}()
}
@@ -379,7 +381,7 @@ func SendVerifyEmailAndForget(userId, userEmail, teamName, teamDisplayName, site
bodyPage.Props["VerifyUrl"] = link
if err := utils.SendMail(userEmail, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("Failed to send verification email successfully err=%v", err)
l4g.Error(utils.T("api.user.send_verify_email_and_forget.failed.error"), err)
}
}()
}
@@ -417,7 +419,8 @@ func LoginByEmail(c *Context, w http.ResponseWriter, r *http.Request, email, nam
user := result.Data.(*model.User)
if len(user.AuthData) != 0 {
c.Err = model.NewAppError("LoginByEmail", "Please sign in using "+user.AuthService, "")
c.Err = model.NewLocAppError("LoginByEmail", "api.user.login_by_email.sign_in.app_error",
map[string]interface{}{"AuthService": user.AuthService}, "")
return nil
}
@@ -434,14 +437,16 @@ func LoginByOAuth(c *Context, w http.ResponseWriter, r *http.Request, service st
authData := ""
provider := einterfaces.GetOauthProvider(service)
if provider == nil {
c.Err = model.NewAppError("LoginByOAuth", service+" oauth not avlailable on this server", "")
c.Err = model.NewLocAppError("LoginByOAuth", "api.user.login_by_oauth.not_available.app_error",
map[string]interface{}{"Service": service}, "")
return nil
} else {
authData = provider.GetAuthDataFromJson(userData)
}
if len(authData) == 0 {
c.Err = model.NewAppError("LoginByOAuth", "Could not parse auth data out of "+service+" user object", "")
c.Err = model.NewLocAppError("LoginByOAuth", "api.user.login_by_oauth.parse.app_error",
map[string]interface{}{"Service": service}, "")
return nil
}
@@ -459,7 +464,7 @@ func LoginByOAuth(c *Context, w http.ResponseWriter, r *http.Request, service st
func checkUserLoginAttempts(c *Context, user *model.User) bool {
if user.FailedAttempts >= utils.Cfg.ServiceSettings.MaximumLoginAttempts {
c.LogAuditWithUserId(user.Id, "fail")
c.Err = model.NewAppError("checkUserLoginAttempts", "Your account is locked because of too many failed password attempts. Please reset your password.", "user_id="+user.Id)
c.Err = model.NewLocAppError("checkUserLoginAttempts", "api.user.check_user_login_attempts.too_many.app_error", nil, "user_id="+user.Id)
c.Err.StatusCode = http.StatusForbidden
return false
}
@@ -471,7 +476,7 @@ func checkUserPassword(c *Context, user *model.User, password string) bool {
if !model.ComparePassword(user.Password, password) {
c.LogAuditWithUserId(user.Id, "fail")
c.Err = model.NewAppError("checkUserPassword", "Login failed because of invalid password", "user_id="+user.Id)
c.Err = model.NewLocAppError("checkUserPassword", "api.user.check_user_password.invalid.app_error", nil, "user_id="+user.Id)
c.Err.StatusCode = http.StatusForbidden
if result := <-Srv.Store.User().UpdateFailedPasswordAttempts(user.Id, user.FailedAttempts+1); result.Err != nil {
@@ -494,13 +499,13 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User,
c.LogAuditWithUserId(user.Id, "attempt")
if !user.EmailVerified && utils.Cfg.EmailSettings.RequireEmailVerification {
c.Err = model.NewAppError("Login", "Login failed because email address has not been verified", "user_id="+user.Id)
c.Err = model.NewLocAppError("Login", "api.user.login.not_verified.app_error", nil, "user_id="+user.Id)
c.Err.StatusCode = http.StatusForbidden
return
}
if user.DeleteAt > 0 {
c.Err = model.NewAppError("Login", "Login failed because your account has been set to inactive. Please contact an administrator.", "user_id="+user.Id)
c.Err = model.NewLocAppError("Login", "api.user.login.inactive.app_error", nil, "user_id="+user.Id)
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -522,7 +527,7 @@ func Login(c *Context, w http.ResponseWriter, r *http.Request, user *model.User,
sessions := result.Data.([]*model.Session)
for _, session := range sessions {
if session.DeviceId == deviceId {
l4g.Debug("Revoking sessionId=" + session.Id + " for userId=" + user.Id + " re-login with same device Id")
l4g.Debug(utils.T("api.user.login.revoking.app_error"), session.Id, user.Id)
RevokeSessionById(c, session.Id)
if c.Err != nil {
c.LogError(c.Err)
@@ -604,7 +609,7 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
props := model.MapFromJson(r.Body)
if len(props["password"]) == 0 {
c.Err = model.NewAppError("login", "Password field must not be blank", "")
c.Err = model.NewLocAppError("login", "api.user.login.blank_pwd.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -615,7 +620,7 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
} else if len(props["email"]) != 0 && len(props["name"]) != 0 {
user = LoginByEmail(c, w, r, props["email"], props["name"], props["password"], props["device_id"])
} else {
c.Err = model.NewAppError("login", "Either user id or team name and user email must be provided", "")
c.Err = model.NewLocAppError("login", "api.user.login.not_provided.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -634,7 +639,7 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) {
func loginLdap(c *Context, w http.ResponseWriter, r *http.Request) {
if !*utils.Cfg.LdapSettings.Enable {
c.Err = model.NewAppError("loginLdap", "LDAP not enabled on this server", "")
c.Err = model.NewLocAppError("loginLdap", "api.user.login_ldap.disabled.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
@@ -646,13 +651,13 @@ func loginLdap(c *Context, w http.ResponseWriter, r *http.Request) {
teamName := props["teamName"]
if len(password) == 0 {
c.Err = model.NewAppError("loginLdap", "Password field must not be blank", "")
c.Err = model.NewLocAppError("loginLdap", "api.user.login_ldap.blank_pwd.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
if len(id) == 0 {
c.Err = model.NewAppError("loginLdap", "Need an ID", "")
c.Err = model.NewLocAppError("loginLdap", "api.user.login_ldap.need_id.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -661,7 +666,7 @@ func loginLdap(c *Context, w http.ResponseWriter, r *http.Request) {
ldapInterface := einterfaces.GetLdapInterface()
if ldapInterface == nil {
c.Err = model.NewAppError("loginLdap", "LDAP not available on this server", "")
c.Err = model.NewLocAppError("loginLdap", "api.user.login_ldap.not_available.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
@@ -794,7 +799,7 @@ func getMe(c *Context, w http.ResponseWriter, r *http.Request) {
if result := <-Srv.Store.User().Get(c.Session.UserId); result.Err != nil {
c.Err = result.Err
c.RemoveSessionCookie(w, r)
l4g.Error("Error in getting users profile for id=%v forcing logout", c.Session.UserId)
l4g.Error(utils.T("api.user.get_me.getting.error"), c.Session.UserId)
return
} else if HandleEtag(result.Data.(*model.User).Etag(), w, r) {
return
@@ -947,11 +952,11 @@ func createProfileImage(username string, userId string) ([]byte, *model.AppError
fontBytes, err := ioutil.ReadFile(utils.FindDir("web/static/fonts") + utils.Cfg.FileSettings.InitialFont)
if err != nil {
return nil, model.NewAppError("createProfileImage", "Could not create default profile image font", err.Error())
return nil, model.NewLocAppError("createProfileImage", "api.user.create_profile_image.default_font.app_error", nil, err.Error())
}
font, err := freetype.ParseFont(fontBytes)
if err != nil {
return nil, model.NewAppError("createProfileImage", "Could not create default profile image font", err.Error())
return nil, model.NewLocAppError("createProfileImage", "api.user.create_profile_image.default_font.app_error", nil, err.Error())
}
width := int(utils.Cfg.FileSettings.ProfileWidth)
@@ -972,13 +977,13 @@ func createProfileImage(username string, userId string) ([]byte, *model.AppError
pt := freetype.Pt(width/6, height*2/3)
_, err = c.DrawString(initial, pt)
if err != nil {
return nil, model.NewAppError("createProfileImage", "Could not add user initial to default profile picture", err.Error())
return nil, model.NewLocAppError("createProfileImage", "api.user.create_profile_image.initial.app_error", nil, err.Error())
}
buf := new(bytes.Buffer)
if imgErr := png.Encode(buf, dstImg); imgErr != nil {
return nil, model.NewAppError("createProfileImage", "Could not encode default profile image", imgErr.Error())
return nil, model.NewLocAppError("createProfileImage", "api.user.create_profile_image.encode.app_error", nil, imgErr.Error())
} else {
return buf.Bytes(), nil
}
@@ -1033,13 +1038,13 @@ func getProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
if len(utils.Cfg.FileSettings.DriverName) == 0 {
c.Err = model.NewAppError("uploadProfileImage", "Unable to upload file. Image storage is not configured.", "")
c.Err = model.NewLocAppError("uploadProfileImage", "api.user.upload_profile_user.storage.app_error", nil, "")
c.Err.StatusCode = http.StatusNotImplemented
return
}
if err := r.ParseMultipartForm(10000000); err != nil {
c.Err = model.NewAppError("uploadProfileImage", "Could not parse multipart form", "")
c.Err = model.NewLocAppError("uploadProfileImage", "api.user.upload_profile_user.parse.app_error", nil, "")
return
}
@@ -1047,13 +1052,13 @@ func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
imageArray, ok := m.File["image"]
if !ok {
c.Err = model.NewAppError("uploadProfileImage", "No file under 'image' in request", "")
c.Err = model.NewLocAppError("uploadProfileImage", "api.user.upload_profile_user.no_file.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}
if len(imageArray) <= 0 {
c.Err = model.NewAppError("uploadProfileImage", "Empty array under 'image' in request", "")
c.Err = model.NewLocAppError("uploadProfileImage", "api.user.upload_profile_user.array.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}
@@ -1063,17 +1068,17 @@ func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
file, err := imageData.Open()
defer file.Close()
if err != nil {
c.Err = model.NewAppError("uploadProfileImage", "Could not open image file", err.Error())
c.Err = model.NewLocAppError("uploadProfileImage", "api.user.upload_profile_user.open.app_error", nil, err.Error())
return
}
// Decode image config first to check dimensions before loading the whole thing into memory later on
config, _, err := image.DecodeConfig(file)
if err != nil {
c.Err = model.NewAppError("uploadProfileFile", "Could not decode profile image config.", err.Error())
c.Err = model.NewLocAppError("uploadProfileFile", "api.user.upload_profile_user.decode_config.app_error", nil, err.Error())
return
} else if config.Width*config.Height > MaxImageSize {
c.Err = model.NewAppError("uploadProfileFile", "Unable to upload profile image. File is too large.", err.Error())
c.Err = model.NewLocAppError("uploadProfileFile", "api.user.upload_profile_user.too_large.app_error", nil, err.Error())
return
}
@@ -1082,7 +1087,7 @@ func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
// Decode image into Image object
img, _, err := image.Decode(file)
if err != nil {
c.Err = model.NewAppError("uploadProfileImage", "Could not decode profile image", err.Error())
c.Err = model.NewLocAppError("uploadProfileImage", "api.user.upload_profile_user.decode.app_error", nil, err.Error())
return
}
@@ -1092,7 +1097,7 @@ func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
buf := new(bytes.Buffer)
err = png.Encode(buf, img)
if err != nil {
c.Err = model.NewAppError("uploadProfileImage", "Could not encode profile image", err.Error())
c.Err = model.NewLocAppError("uploadProfileImage", "api.user.upload_profile_user.encode.app_error", nil, err.Error())
return
}
@@ -1173,7 +1178,7 @@ func updatePassword(c *Context, w http.ResponseWriter, r *http.Request) {
}
if userId != c.Session.UserId {
c.Err = model.NewAppError("updatePassword", "Update password failed because context user_id did not match props user_id", "")
c.Err = model.NewLocAppError("updatePassword", "api.user.update_password.context.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -1186,7 +1191,7 @@ func updatePassword(c *Context, w http.ResponseWriter, r *http.Request) {
}
if result.Data == nil {
c.Err = model.NewAppError("updatePassword", "Update password failed because we couldn't find a valid account", "")
c.Err = model.NewLocAppError("updatePassword", "api.user.update_password.valid_account.app_error", nil, "")
c.Err.StatusCode = http.StatusBadRequest
return
}
@@ -1197,19 +1202,19 @@ func updatePassword(c *Context, w http.ResponseWriter, r *http.Request) {
if user.AuthData != "" {
c.LogAudit("failed - tried to update user password who was logged in through oauth")
c.Err = model.NewAppError("updatePassword", "Update password failed because the user is logged in through an OAuth service", "auth_service="+user.AuthService)
c.Err = model.NewLocAppError("updatePassword", "api.user.update_password.oauth.app_error", nil, "auth_service="+user.AuthService)
c.Err.StatusCode = http.StatusForbidden
return
}
if !model.ComparePassword(user.Password, currentPassword) {
c.Err = model.NewAppError("updatePassword", "The \"Current Password\" you entered is incorrect. Please check that Caps Lock is off and try again.", "")
c.Err = model.NewLocAppError("updatePassword", "api.user.update_password.incorrect.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
if uresult := <-Srv.Store.User().UpdatePassword(c.Session.UserId, model.HashPassword(newPassword)); uresult.Err != nil {
c.Err = model.NewAppError("updatePassword", "Update password failed", uresult.Err.Error())
c.Err = model.NewLocAppError("updatePassword", "api.user.update_password.failed.app_error", nil, uresult.Err.Error())
c.Err.StatusCode = http.StatusForbidden
return
} else {
@@ -1219,7 +1224,7 @@ func updatePassword(c *Context, w http.ResponseWriter, r *http.Request) {
l4g.Error(tresult.Err.Message)
} else {
team := tresult.Data.(*model.Team)
sendPasswordChangeEmailAndForget(user.Email, team.DisplayName, c.GetTeamURLFromTeam(team), c.GetSiteURL(), "using the settings menu")
sendPasswordChangeEmailAndForget(user.Email, team.DisplayName, c.GetTeamURLFromTeam(team), c.GetSiteURL(), c.T("api.user.update_password.menu"))
}
data := make(map[string]string)
@@ -1244,7 +1249,7 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) {
}
if model.IsInRole(new_roles, model.ROLE_SYSTEM_ADMIN) && !c.IsSystemAdmin() {
c.Err = model.NewAppError("updateRoles", "The system admin role can only be set by another system admin", "")
c.Err = model.NewLocAppError("updateRoles", "api.user.update_roles.system_admin_set.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -1262,13 +1267,13 @@ func updateRoles(c *Context, w http.ResponseWriter, r *http.Request) {
}
if !c.IsTeamAdmin() {
c.Err = model.NewAppError("updateRoles", "You do not have the appropriate permissions", "userId="+user_id)
c.Err = model.NewLocAppError("updateRoles", "api.user.update_roles.permissions.app_error", nil, "userId="+user_id)
c.Err.StatusCode = http.StatusForbidden
return
}
if user.IsInRole(model.ROLE_SYSTEM_ADMIN) && !c.IsSystemAdmin() {
c.Err = model.NewAppError("updateRoles", "The system admin role can only by modified by another system admin", "")
c.Err = model.NewLocAppError("updateRoles", "api.user.update_roles.system_admin_mod.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -1320,7 +1325,7 @@ func UpdateRoles(c *Context, user *model.User, roles string) *model.User {
}
if activeAdmins <= 0 {
c.Err = model.NewAppError("updateRoles", "There must be at least one active admin", "")
c.Err = model.NewLocAppError("updateRoles", "api.user.update_roles.one_admin.app_error", nil, "")
return nil
}
}
@@ -1365,7 +1370,7 @@ func updateActive(c *Context, w http.ResponseWriter, r *http.Request) {
}
if !c.IsTeamAdmin() {
c.Err = model.NewAppError("updateActive", "You do not have the appropriate permissions", "userId="+user_id)
c.Err = model.NewLocAppError("updateActive", "api.user.update_active.permissions.app_error", nil, "userId="+user_id)
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -1385,7 +1390,7 @@ func updateActive(c *Context, w http.ResponseWriter, r *http.Request) {
}
if activeAdmins <= 0 {
c.Err = model.NewAppError("updateRoles", "There must be at least one active admin", "userId="+user_id)
c.Err = model.NewLocAppError("updateRoles", "api.user.update_roles.one_admin.app_error", nil, "userId="+user_id)
return
}
}
@@ -1424,12 +1429,12 @@ func UpdateActive(c *Context, user *model.User, active bool) *model.User {
}
func PermanentDeleteUser(c *Context, user *model.User) *model.AppError {
l4g.Warn("Attempting to permanently delete account %v id=%v", user.Email, user.Id)
l4g.Warn(utils.T("api.user.permanent_delete_user.attempting.warn"), user.Email, user.Id)
c.Path = "/users/permanent_delete"
c.LogAuditWithUserId(user.Id, fmt.Sprintf("attempt userId=%v", user.Id))
c.LogAuditWithUserId("", fmt.Sprintf("attempt userId=%v", user.Id))
if user.IsInRole(model.ROLE_SYSTEM_ADMIN) {
l4g.Warn("You are deleting %v that is a system administrator. You may need to set another account as the system administrator using the command line tools.", user.Email)
l4g.Warn(utils.T("api.user.permanent_delete_user.system_admin.warn"), user.Email)
}
UpdateActive(c, user, false)
@@ -1474,7 +1479,7 @@ func PermanentDeleteUser(c *Context, user *model.User) *model.AppError {
return result.Err
}
l4g.Warn("Permanently deleted account %v id=%v", user.Email, user.Id)
l4g.Warn(utils.T("api.user.permanent_delete_user.deleted.warn"), user.Email, user.Id)
c.LogAuditWithUserId("", fmt.Sprintf("success userId=%v", user.Id))
return nil
@@ -1505,14 +1510,14 @@ func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) {
var user *model.User
if result := <-Srv.Store.User().GetByEmail(team.Id, email); result.Err != nil {
c.Err = model.NewAppError("sendPasswordReset", "We couldnt find an account with that address.", "email="+email+" team_id="+team.Id)
c.Err = model.NewLocAppError("sendPasswordReset", "api.user.send_password_reset.find.app_error", nil, "email="+email+" team_id="+team.Id)
return
} else {
user = result.Data.(*model.User)
}
if len(user.AuthData) != 0 {
c.Err = model.NewAppError("sendPasswordReset", "Cannot reset password for SSO accounts", "userId="+user.Id+", teamId="+team.Id)
c.Err = model.NewLocAppError("sendPasswordReset", "api.user.send_password_reset.sso.app_error", nil, "userId="+user.Id+", teamId="+team.Id)
return
}
@@ -1532,7 +1537,7 @@ func sendPasswordReset(c *Context, w http.ResponseWriter, r *http.Request) {
bodyPage.Props["ResetUrl"] = link
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
c.Err = model.NewAppError("sendPasswordReset", "Failed to send password reset email successfully", "err="+err.Message)
c.Err = model.NewLocAppError("sendPasswordReset", "api.user.send_password_reset.send.app_error", nil, "err="+err.Message)
return
}
@@ -1601,25 +1606,25 @@ func resetPassword(c *Context, w http.ResponseWriter, r *http.Request) {
}
if len(user.AuthData) != 0 {
c.Err = model.NewAppError("resetPassword", "Cannot reset password for SSO accounts", "userId="+user.Id+", teamId="+team.Id)
c.Err = model.NewLocAppError("resetPassword", "api.user.reset_password.sso.app_error", nil, "userId="+user.Id+", teamId="+team.Id)
return
}
if user.TeamId != team.Id {
c.Err = model.NewAppError("resetPassword", "Trying to reset password for user on wrong team.", "userId="+user.Id+", teamId="+team.Id)
c.Err = model.NewLocAppError("resetPassword", "api.user.reset_password.wrong_team.app_error", nil, "userId="+user.Id+", teamId="+team.Id)
c.Err.StatusCode = http.StatusForbidden
return
}
if !c.IsSystemAdmin() {
if !model.ComparePassword(hash, fmt.Sprintf("%v:%v", props["data"], utils.Cfg.EmailSettings.PasswordResetSalt)) {
c.Err = model.NewAppError("resetPassword", "The reset password link does not appear to be valid", "")
c.Err = model.NewLocAppError("resetPassword", "api.user.reset_password.invalid_link.app_error", nil, "")
return
}
t, err := strconv.ParseInt(timeStr, 10, 64)
if err != nil || model.GetMillis()-t > 1000*60*60 { // one hour
c.Err = model.NewAppError("resetPassword", "The reset link has expired", "")
c.Err = model.NewLocAppError("resetPassword", "api.user.reset_password.link_expired.app_error", nil, "")
return
}
}
@@ -1650,7 +1655,7 @@ func sendPasswordChangeEmailAndForget(email, teamDisplayName, teamURL, siteURL,
bodyPage.Props["Method"] = method
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("Failed to send update password email successfully err=%v", err)
l4g.Error(utils.T("api.user.send_password_change_email_and_forget.error"), err)
}
}()
@@ -1669,7 +1674,7 @@ func sendEmailChangeEmailAndForget(oldEmail, newEmail, teamDisplayName, teamURL,
bodyPage.Props["NewEmail"] = newEmail
if err := utils.SendMail(oldEmail, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("Failed to send email change notification email successfully err=%v", err)
l4g.Error(utils.T("api.user.send_email_change_email_and_forget.error"), err)
}
}()
@@ -1689,7 +1694,7 @@ func SendEmailChangeVerifyEmailAndForget(userId, newUserEmail, teamName, teamDis
bodyPage.Props["VerifyUrl"] = link
if err := utils.SendMail(newUserEmail, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("Failed to send email change verification email successfully err=%v", err)
l4g.Error(utils.T("api.user.send_email_change_verify_email_and_forget.error"), err)
}
}()
}
@@ -1798,7 +1803,7 @@ func GetAuthorizationCode(c *Context, service, teamName string, props map[string
sso := utils.Cfg.GetSSOService(service)
if sso != nil && !sso.Enable {
return "", model.NewAppError("GetAuthorizationCode", "Unsupported OAuth service provider", "service="+service)
return "", model.NewLocAppError("GetAuthorizationCode", "api.user.get_authorization_code.unsupported.app_error", nil, "service="+service)
}
clientId := sso.Id
@@ -1827,12 +1832,12 @@ func GetAuthorizationCode(c *Context, service, teamName string, props map[string
func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser, *model.Team, map[string]string, *model.AppError) {
sso := utils.Cfg.GetSSOService(service)
if sso == nil || !sso.Enable {
return nil, nil, nil, model.NewAppError("AuthorizeOAuthUser", "Unsupported OAuth service provider", "service="+service)
return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.unsupported.app_error", nil, "service="+service)
}
stateStr := ""
if b, err := b64.StdEncoding.DecodeString(state); err != nil {
return nil, nil, nil, model.NewAppError("AuthorizeOAuthUser", "Invalid state", err.Error())
return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, err.Error())
} else {
stateStr = string(b)
}
@@ -1840,13 +1845,13 @@ func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser
stateProps := model.MapFromJson(strings.NewReader(stateStr))
if !model.ComparePassword(stateProps["hash"], sso.Id) {
return nil, nil, nil, model.NewAppError("AuthorizeOAuthUser", "Invalid state", "")
return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state.app_error", nil, "")
}
ok := true
teamName := ""
if teamName, ok = stateProps["team"]; !ok {
return nil, nil, nil, model.NewAppError("AuthorizeOAuthUser", "Invalid state; missing team name", "")
return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.invalid_state_team.app_error", nil, "")
}
tchan := Srv.Store.Team().GetByName(teamName)
@@ -1866,20 +1871,20 @@ func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser
var ar *model.AccessResponse
if resp, err := client.Do(req); err != nil {
return nil, nil, nil, model.NewAppError("AuthorizeOAuthUser", "Token request failed", err.Error())
return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.token_failed.app_error", nil, err.Error())
} else {
ar = model.AccessResponseFromJson(resp.Body)
if ar == nil {
return nil, nil, nil, model.NewAppError("AuthorizeOAuthUser", "Bad response from token request", "")
return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_response.app_error", nil, "")
}
}
if strings.ToLower(ar.TokenType) != model.ACCESS_TOKEN_TYPE {
return nil, nil, nil, model.NewAppError("AuthorizeOAuthUser", "Bad token type", "token_type="+ar.TokenType)
return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_token.app_error", nil, "token_type="+ar.TokenType)
}
if len(ar.AccessToken) == 0 {
return nil, nil, nil, model.NewAppError("AuthorizeOAuthUser", "Missing access token", "")
return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.missing.app_error", nil, "")
}
p = url.Values{}
@@ -1891,7 +1896,8 @@ func AuthorizeOAuthUser(service, code, state, redirectUri string) (io.ReadCloser
req.Header.Set("Authorization", "Bearer "+ar.AccessToken)
if resp, err := client.Do(req); err != nil {
return nil, nil, nil, model.NewAppError("AuthorizeOAuthUser", "Token request to "+service+" failed", err.Error())
return nil, nil, nil, model.NewLocAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.service.app_error",
map[string]interface{}{"Service": service}, err.Error())
} else {
if result := <-tchan; result.Err != nil {
return nil, nil, nil, result.Err
@@ -1990,19 +1996,21 @@ func CompleteSwitchWithOAuth(c *Context, w http.ResponseWriter, r *http.Request,
authData := ""
provider := einterfaces.GetOauthProvider(service)
if provider == nil {
c.Err = model.NewAppError("CompleteClaimWithOAuth", service+" oauth not avlailable on this server", "")
c.Err = model.NewLocAppError("CompleteClaimWithOAuth", "api.user.complete_switch_with_oauth.unavailable.app_error",
map[string]interface{}{"Service": service}, "")
return
} else {
authData = provider.GetAuthDataFromJson(userData)
}
if len(authData) == 0 {
c.Err = model.NewAppError("CompleteClaimWithOAuth", "Could not parse auth data out of "+service+" user object", "")
c.Err = model.NewLocAppError("CompleteClaimWithOAuth", "api.user.complete_switch_with_oauth.parse.app_error",
map[string]interface{}{"Service": service}, "")
return
}
if len(email) == 0 {
c.Err = model.NewAppError("CompleteClaimWithOAuth", "Blank email", "")
c.Err = model.NewLocAppError("CompleteClaimWithOAuth", "api.user.complete_switch_with_oauth.blank_email.app_error", nil, "")
return
}
@@ -2070,7 +2078,7 @@ func switchToEmail(c *Context, w http.ResponseWriter, r *http.Request) {
if user.Id != c.Session.UserId {
c.LogAudit("fail - user ids didn't match")
c.Err = model.NewAppError("switchToEmail", "Update password failed because context user_id did not match provided user's id", "")
c.Err = model.NewLocAppError("switchToEmail", "api.user.switch_to_email.context.app_error", nil, "")
c.Err.StatusCode = http.StatusForbidden
return
}
@@ -2108,7 +2116,7 @@ func sendSignInChangeEmailAndForget(email, teamDisplayName, teamURL, siteURL, me
bodyPage.Props["Method"] = method
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("Failed to send update password email successfully err=%v", err)
l4g.Error(utils.T("api.user.send_sign_in_change_email_and_forget.error"), err)
}
}()

Просмотреть файл

@@ -110,4 +110,4 @@
"TokenEndpoint": "",
"UserApiEndpoint": ""
}
}
}

Просмотреть файл

@@ -40,7 +40,6 @@ http://
./make-compiled-client.sh
test.:test
https://<your-mattermost-url>/signup/gitlab
https://your-mattermost-url>/signup/gitlab
#### Only the links within these sentences should auto-link:

Просмотреть файл

@@ -88,7 +88,7 @@ and
## Emojis
Emoji provided free by [Emoji One](http://emojione.com/). Check out a full list of emojis [here](http://http://emoji.codes/).
Emoji provided free by [Emoji One](http://emojione.com/). Check out a full list of emojis [here](http://emoji.codes/).
```
:smile: :+1: :sheep:

Просмотреть файл

@@ -1,6 +1,6 @@
## AWS Elastic Beanstalk Setup (Docker)
These instructions will guide you through the process of setting up Mattermost for product evaluation using an EBS Docker single-container application using [Dockerrun.aws.zip](https://github.com/mattermost/platform/raw/master/docker/1.1/Dockerrun.aws.zip).
These instructions will guide you through the process of setting up Mattermost for product evaluation using an EBS Docker single-container application using [Dockerrun.aws.zip][Dockerrun.aws].
1. From your [AWS console]( https://console.aws.amazon.com/console/home) select **Elastic Beanstalk** under the Compute section.
2. Select **Create New Application** from the top right.
@@ -11,7 +11,7 @@ These instructions will guide you through the process of setting up Mattermost f
1. Set Predefined Configuration to **Docker** under the generic heading in the drop-down list.
2. Set Environment Type to **Single instance** in the drop-down list.
3. Click **Next**.
7. For Application Source, select **Upload your own** and upload the [Dockerrun.aws.zip](https://github.com/mattermost/platform/raw/master/docker/1.1/Dockerrun.aws.zip) file, then click **Next**.
7. For Application Source, select **Upload your own** and upload the [Dockerrun.aws.zip][Dockerrun.aws] file, then click **Next**.
8. Type an Environment Name and URL. Make sure the URL is available by clicking **Check availability**, then click **Next**.
9. The options on the Additional Resources page may be left at default unless you wish to change them. Click **Next**.
10. On the Configuration Details page,
@@ -24,3 +24,5 @@ These instructions will guide you through the process of setting up Mattermost f
### (Recommended) Enable Email
The default single-container Docker instance for Mattermost is designed for product evaluation, and sets `SendEmailNotifications=false` so the product can function without enabling email. To see the product's full functionality, [enabling SMTP email is recommended](SMTP-Email-Setup.md).
[Dockerrun.aws]: https://github.com/mattermost/platform/raw/master/docker/1.4/Dockerrun.aws.zip

Просмотреть файл

@@ -41,6 +41,13 @@ Set the number of days before SSO sessions expire.
```"SessionCacheInMinutes" : 10```
Set the number of minutes to cache a session in memory.
```"WebsocketSecurePort": 443```
The port to use for secure websocket connections being initiated from the client. By default wss:// uses port 443. Some server configurations (e.g. Cloudfoundry) support wss on a different port.
```"WebsocketPort": 80```
The port to use for websocket connections being initiated from the client. By default ws:// uses port 80.
#### Webhooks
```"EnableIncomingWebhooks": true```

Просмотреть файл

@@ -44,7 +44,7 @@
1. For the sake of making this guide simple we located the files at `/home/ubuntu/mattermost`. In the future we will give guidance for storing under `/opt`.
1. We have also elected to run the Mattermost Server as the `ubuntu` account for simplicity. We recommend setting up and running the service under a `mattermost` user account with limited permissions.
1. Download the latest Mattermost Server by typing:
* ``` wget https://github.com/mattermost/platform/releases/download/v1.3.0/mattermost.tar.gz```
* ``` wget https://github.com/mattermost/platform/releases/download/v1.4.0/mattermost.tar.gz```
1. Unzip the Mattermost Server by typing:
* ``` tar -xvzf mattermost.tar.gz```
1. Create the storage directory for files. We assume you will have attached a large drive for storage of images and files. For this setup we will assume the directory is located at `/mattermost/data`.

Двоичные данные
docker/1.4/Dockerrun.aws.zip

Двоичный файл не отображается.

1330
i18n/en.json Обычный файл

Разница между файлами не показана из-за своего большого размера Загрузить разницу

1330
i18n/es.json Обычный файл

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@@ -50,14 +50,15 @@ func main() {
parseCmds()
utils.LoadConfig(flagConfigFile)
utils.InitTranslations()
if flagRunCmds {
utils.ConfigureCmdLineLog()
}
pwd, _ := os.Getwd()
l4g.Info("Current version is %v (%v/%v/%v)", model.CurrentVersion, model.BuildNumber, model.BuildDate, model.BuildHash)
l4g.Info("Enterprise Enabled: %t", model.BuildEnterpriseReady)
l4g.Info(utils.T("mattermost.current_version"), model.CurrentVersion, model.BuildNumber, model.BuildDate, model.BuildHash)
l4g.Info("Enterprise Enabled: %v", model.BuildEnterpriseReady)
l4g.Info("Current working directory is %v", pwd)
l4g.Info("Loaded config file from %v", utils.FindConfigFile(flagConfigFile))

Просмотреть файл

@@ -42,6 +42,8 @@ type ServiceSettings struct {
SessionLengthMobileInDays *int
SessionLengthSSOInDays *int
SessionCacheInMinutes *int
WebsocketSecurePort *int
WebsocketPort *int
}
type SSOSettings struct {
@@ -342,6 +344,16 @@ func (o *Config) SetDefaults() {
o.ServiceSettings.EnableOnlyAdminIntegrations = new(bool)
*o.ServiceSettings.EnableOnlyAdminIntegrations = true
}
if o.ServiceSettings.WebsocketPort == nil {
o.ServiceSettings.WebsocketPort = new(int)
*o.ServiceSettings.WebsocketPort = 80
}
if o.ServiceSettings.WebsocketSecurePort == nil {
o.ServiceSettings.WebsocketSecurePort = new(int)
*o.ServiceSettings.WebsocketSecurePort = 443
}
}
func (o *Config) IsValid() *AppError {

Просмотреть файл

@@ -6,11 +6,12 @@ package model
import (
"encoding/json"
"fmt"
"golang.org/x/crypto/bcrypt"
"io"
"regexp"
"strings"
"unicode/utf8"
"golang.org/x/crypto/bcrypt"
)
const (
@@ -24,6 +25,7 @@ const (
USER_NOTIFY_ALL = "all"
USER_NOTIFY_MENTION = "mention"
USER_NOTIFY_NONE = "none"
DEFAULT_LOCALE = "en"
)
type User struct {
@@ -51,6 +53,7 @@ type User struct {
LastPasswordUpdate int64 `json:"last_password_update,omitempty"`
LastPictureUpdate int64 `json:"last_picture_update,omitempty"`
FailedAttempts int `json:"failed_attempts,omitempty"`
Locale string `json:"locale"`
}
// IsValid validates the user and returns an error if it isn't configured
@@ -130,12 +133,17 @@ func (u *User) PreSave() {
u.Username = strings.ToLower(u.Username)
u.Email = strings.ToLower(u.Email)
u.Locale = strings.ToLower(u.Locale)
u.CreateAt = GetMillis()
u.UpdateAt = u.CreateAt
u.LastPasswordUpdate = u.CreateAt
if u.Locale == "" {
u.Locale = DEFAULT_LOCALE
}
if u.Props == nil {
u.Props = make(map[string]string)
}
@@ -153,6 +161,7 @@ func (u *User) PreSave() {
func (u *User) PreUpdate() {
u.Username = strings.ToLower(u.Username)
u.Email = strings.ToLower(u.Email)
u.Locale = strings.ToLower(u.Locale)
u.UpdateAt = GetMillis()
if u.NotifyProps == nil || len(u.NotifyProps) == 0 {

Просмотреть файл

@@ -9,13 +9,15 @@ import (
"encoding/base32"
"encoding/json"
"fmt"
"github.com/pborman/uuid"
"io"
"net/mail"
"net/url"
"regexp"
"strings"
"time"
goi18n "github.com/nicksnyder/go-i18n/i18n"
"github.com/pborman/uuid"
)
type StringInterface map[string]interface{}
@@ -23,20 +25,31 @@ type StringMap map[string]string
type StringArray []string
type EncryptStringMap map[string]string
// AppError is returned for any http response that's not in the 200 range.
type AppError struct {
Message string `json:"message"` // Message to be display to the end user without debugging information
DetailedError string `json:"detailed_error"` // Internal error string to help the developer
RequestId string `json:"request_id"` // The RequestId that's also set in the header
StatusCode int `json:"status_code"` // The http status code
Where string `json:"-"` // The function where it happened in the form of Struct.Func
IsOAuth bool `json:"is_oauth"` // Whether the error is OAuth specific
Id string `json:"id"`
Message string `json:"message"` // Message to be display to the end user without debugging information
DetailedError string `json:"detailed_error"` // Internal error string to help the developer
RequestId string `json:"request_id"` // The RequestId that's also set in the header
StatusCode int `json:"status_code"` // The http status code
Where string `json:"-"` // The function where it happened in the form of Struct.Func
IsOAuth bool `json:"is_oauth"` // Whether the error is OAuth specific
params map[string]interface{} `json:"-"`
}
func (er *AppError) Error() string {
return er.Where + ": " + er.Message + ", " + er.DetailedError
}
func (er *AppError) Translate(T goi18n.TranslateFunc) {
if len(er.Message) == 0 {
if er.params == nil {
er.Message = T(er.Id)
} else {
er.Message = T(er.Id, er.params)
}
}
}
func (er *AppError) ToJson() string {
b, err := json.Marshal(er)
if err != nil {
@@ -72,6 +85,17 @@ func NewAppError(where string, message string, details string) *AppError {
return ap
}
func NewLocAppError(where string, id string, params map[string]interface{}, details string) *AppError {
ap := &AppError{}
ap.Id = id
ap.params = params
ap.Where = where
ap.DetailedError = details
ap.StatusCode = 500
ap.IsOAuth = false
return ap
}
var encoding = base32.NewEncoding("ybndrfg8ejkmcpqxot1uwisza345h769")
// NewId is a globally unique identifier. It is a [A-Z0-9] string 26

Просмотреть файл

@@ -37,6 +37,7 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore {
table.ColMap("Props").SetMaxSize(4000)
table.ColMap("NotifyProps").SetMaxSize(2000)
table.ColMap("ThemeProps").SetMaxSize(2000)
table.ColMap("Locale").SetMaxSize(5)
table.SetUniqueTogether("Email", "TeamId")
table.SetUniqueTogether("Username", "TeamId")
}
@@ -45,6 +46,7 @@ func NewSqlUserStore(sqlStore *SqlStore) UserStore {
}
func (us SqlUserStore) UpgradeSchemaIfNeeded() {
us.CreateColumnIfNotExists("Users", "Locale", "varchar(5)", "character varying(5)", model.DEFAULT_LOCALE) // Added After 1.4
}
func (us SqlUserStore) CreateIndexesIfNotExists() {

Просмотреть файл

@@ -225,5 +225,8 @@ func getClientConfig(c *model.Config) map[string]string {
props["EnableLdap"] = strconv.FormatBool(*c.LdapSettings.Enable)
props["WebsocketPort"] = fmt.Sprintf("%v", *c.ServiceSettings.WebsocketPort)
props["WebsocketSecurePort"] = fmt.Sprintf("%v", *c.ServiceSettings.WebsocketSecurePort)
return props
}

79
utils/i18n.go Обычный файл
Просмотреть файл

@@ -0,0 +1,79 @@
package utils
import (
"io/ioutil"
"net/http"
"path/filepath"
"strings"
l4g "github.com/alecthomas/log4go"
"github.com/cloudfoundry/jibber_jabber"
"github.com/mattermost/platform/model"
"github.com/nicksnyder/go-i18n/i18n"
)
var T i18n.TranslateFunc
var locales map[string]string = make(map[string]string)
func InitTranslations() {
i18nDirectory := FindDir("i18n")
files, _ := ioutil.ReadDir(i18nDirectory)
for _, f := range files {
if filepath.Ext(f.Name()) == ".json" {
filename := f.Name()
locales[strings.Split(filename, ".")[0]] = i18nDirectory + filename
i18n.MustLoadTranslationFile(i18nDirectory + filename)
}
}
T = GetTranslationsBySystemLocale()
}
func GetTranslationsBySystemLocale() i18n.TranslateFunc {
locale := model.DEFAULT_LOCALE
if userLanguage, err := jibber_jabber.DetectLanguage(); err == nil {
if _, ok := locales[userLanguage]; ok {
locale = userLanguage
} else {
l4g.Error("Failed to load system translations for '%v' attempting to fall back to '%v'", locale, model.DEFAULT_LOCALE)
locale = model.DEFAULT_LOCALE
}
}
if locales[locale] == "" {
panic("Failed to load system translations for '" + model.DEFAULT_LOCALE + "'")
}
translations, _ := i18n.Tfunc(locale)
if translations == nil {
panic("Failed to load system translations")
}
l4g.Info(translations("utils.i18n.loaded"), locale, locales[locale])
return translations
}
func GetUserTranslations(locale string) i18n.TranslateFunc {
if _, ok := locales[locale]; !ok {
locale = model.DEFAULT_LOCALE
}
translations, _ := i18n.Tfunc(locale)
return translations
}
func SetTranslations(locale string) i18n.TranslateFunc {
translations, _ := i18n.Tfunc(locale)
return translations
}
func GetTranslationsAndLocale(w http.ResponseWriter, r *http.Request) (i18n.TranslateFunc, string) {
headerLocale := strings.Split(strings.Split(r.Header.Get("Accept-Language"), ",")[0], "-")[0]
if locales[headerLocale] != "" {
translations, _ := i18n.Tfunc(headerLocale)
return translations, headerLocale
}
translations, _ := i18n.Tfunc(model.DEFAULT_LOCALE)
return translations, model.DEFAULT_LOCALE
}

Просмотреть файл

@@ -22,6 +22,8 @@
"React": false,
"ReactDOM": false,
"ReactBootstrap": false,
"ReactIntl": false,
"ReactIntlLocaleData": false,
"Chart": false,
"katex": false
},

Просмотреть файл

@@ -2,6 +2,7 @@
// See License.txt for license information.
import * as Client from '../../utils/client.jsx';
import Constants from '../../utils/constants.jsx';
var Modal = ReactBootstrap.Modal;
export default class ResetPasswordModal extends React.Component {
@@ -20,8 +21,8 @@ export default class ResetPasswordModal extends React.Component {
e.preventDefault();
var password = ReactDOM.findDOMNode(this.refs.password).value;
if (!password || password.length < 5) {
this.setState({serverError: 'Please enter at least 5 characters.'});
if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) {
this.setState({serverError: 'Please enter at least ' + Constants.MIN_PASSWORD_LENGTH + ' characters.'});
return;
}

Просмотреть файл

@@ -1,6 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import * as Utils from '../utils/utils.jsx';
const Modal = ReactBootstrap.Modal;
export default class ChannelInfoModal extends React.Component {
@@ -10,10 +11,13 @@ export default class ChannelInfoModal extends React.Component {
channel = {
display_name: 'No Channel Found',
name: 'No Channel Found',
purpose: 'No Channel Found',
id: 'No Channel Found'
};
}
const channelURL = Utils.getShortenedTeamURL() + channel.name;
return (
<Modal
show={this.props.show}
@@ -28,13 +32,17 @@ export default class ChannelInfoModal extends React.Component {
<div className='col-sm-9'>{channel.display_name}</div>
</div>
<div className='row form-group'>
<div className='col-sm-3 info__label'>{'Channel Handle:'}</div>
<div className='col-sm-9'>{channel.name}</div>
<div className='col-sm-3 info__label'>{'Channel URL:'}</div>
<div className='col-sm-9'>{channelURL}</div>
</div>
<div className='row'>
<div className='col-sm-3 info__label'>{'Channel ID:'}</div>
<div className='col-sm-9'>{channel.id}</div>
</div>
<div className='row'>
<div className='col-sm-3 info__label'>{'Channel Purpose:'}</div>
<div className='col-sm-9'>{channel.purpose}</div>
</div>
</Modal.Body>
<Modal.Footer>
<button

Просмотреть файл

@@ -129,7 +129,7 @@ export default class CreateComment extends React.Component {
function handlePostError(err) {
let state = {};
if (err.message === 'Invalid RootId parameter') {
if (err.id === 'api.post.create_post.root_id.app_error') {
PostStore.removePendingPost(post.channel_id, post.pending_post_id);
if ($('#post_deleted').length > 0) {

Просмотреть файл

@@ -186,7 +186,7 @@ export default class CreatePost extends React.Component {
(err) => {
const state = {};
if (err.message === 'Invalid RootId parameter') {
if (err.id === 'api.post.create_post.root_id.app_error') {
if ($('#post_deleted').length > 0) {
$('#post_deleted').modal('show');
}

Просмотреть файл

@@ -151,7 +151,11 @@ export default class FileUpload extends React.Component {
});
}
document.addEventListener('paste', function handlePaste(e) {
document.addEventListener('paste', (e) => {
if (!e.clipboardData) {
return;
}
var textarea = $(inputDiv.parentNode.parentNode).find('.custom-textarea')[0];
if (textarea !== e.target && !$.contains(textarea, e.target)) {

Просмотреть файл

@@ -7,6 +7,8 @@ import LoginLdap from './login_ldap.jsx';
import * as Utils from '../utils/utils.jsx';
import Constants from '../utils/constants.jsx';
var FormattedMessage = ReactIntl.FormattedMessage;
export default class Login extends React.Component {
constructor(props) {
super(props);
@@ -86,7 +88,12 @@ export default class Login extends React.Component {
if (emailSignup) {
forgotPassword = (
<div className='form-group'>
<a href={'/' + teamName + '/reset_password'}>{'I forgot my password'}</a>
<a href={'/' + teamName + '/reset_password'}>
<FormattedMessage
id='login.forgot_password'
defaultMessage='I forgot my password'
/>
</a>
</div>
);
}
@@ -141,7 +148,13 @@ export default class Login extends React.Component {
{ldapLogin}
{userSignUp}
<div className='form-group margin--extra form-group--small'>
<span><a href='/find_team'>{'Find your other teams'}</a></span>
<span>
<a href='/find_team'>
<FormattedMessage
id='login.find_teams'
defaultMessage='Find your other teams'
/>
</a></span>
</div>
{forgotPassword}
{teamSignUp}

Просмотреть файл

@@ -1,7 +1,8 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import * as client from '../utils/client.jsx';
import * as Client from '../utils/client.jsx';
import Constants from '../utils/constants.jsx';
export default class PasswordResetForm extends React.Component {
constructor(props) {
@@ -16,8 +17,8 @@ export default class PasswordResetForm extends React.Component {
var state = {};
var password = ReactDOM.findDOMNode(this.refs.password).value.trim();
if (!password || password.length < 5) {
state.error = 'Please enter at least 5 characters.';
if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) {
state.error = 'Please enter at least ' + Constants.MIN_PASSWORD_LENGTH + ' characters.';
this.setState(state);
return;
}
@@ -31,7 +32,7 @@ export default class PasswordResetForm extends React.Component {
data.data = this.props.data;
data.name = this.props.teamName;
client.resetPassword(data,
Client.resetPassword(data,
function resetSuccess() {
this.setState({error: null, updateText: 'Your password has been updated successfully.'});
}.bind(this),
@@ -59,7 +60,7 @@ export default class PasswordResetForm extends React.Component {
return (
<div className='col-sm-12'>
<div className='signup-team__container'>
<h3>Password Reset</h3>
<h3>{'Password Reset'}</h3>
<form onSubmit={this.handlePasswordReset}>
<p>{'Enter a new password for your ' + this.props.teamDisplayName + ' ' + global.window.mm_config.SiteName + ' account.'}</p>
<div className={formClass}>
@@ -77,7 +78,7 @@ export default class PasswordResetForm extends React.Component {
type='submit'
className='btn btn-primary'
>
Change my password
{'Change my password'}
</button>
{updateText}
</form>

Просмотреть файл

@@ -57,7 +57,10 @@ export default class PostsView extends React.Component {
this.setState({displayNameType: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false')});
}
isAtBottom() {
return ((this.refs.postlist.scrollHeight - this.refs.postlist.scrollTop) === this.refs.postlist.clientHeight);
// consider the view to be at the bottom if it's within this many pixels of the bottom
const atBottomMargin = 10;
return this.refs.postlist.clientHeight + this.refs.postlist.scrollTop >= this.refs.postlist.scrollHeight - atBottomMargin;
}
handleScroll() {
// HACK FOR RHS -- REMOVE WHEN RHS DIES

Просмотреть файл

@@ -17,6 +17,8 @@ export default class RhsThread extends React.Component {
constructor(props) {
super(props);
this.mounted = false;
this.onChange = this.onChange.bind(this);
this.onChangeAll = this.onChangeAll.bind(this);
this.forceUpdateInfo = this.forceUpdateInfo.bind(this);
@@ -50,8 +52,11 @@ export default class RhsThread extends React.Component {
PostStore.addSelectedPostChangeListener(this.onChange);
PostStore.addChangeListener(this.onChangeAll);
PreferenceStore.addChangeListener(this.forceUpdateInfo);
this.resize();
window.addEventListener('resize', this.handleResize);
this.mounted = true;
}
componentDidUpdate() {
if ($('.post-right__scroll')[0]) {
@@ -63,7 +68,10 @@ export default class RhsThread extends React.Component {
PostStore.removeSelectedPostChangeListener(this.onChange);
PostStore.removeChangeListener(this.onChangeAll);
PreferenceStore.removeChangeListener(this.forceUpdateInfo);
window.removeEventListener('resize', this.handleResize);
this.mounted = false;
}
forceUpdateInfo() {
if (this.state.postList) {
@@ -82,7 +90,7 @@ export default class RhsThread extends React.Component {
}
onChange() {
var newState = this.getStateFromStores();
if (!Utils.areObjectsEqual(newState, this.state)) {
if (this.mounted && !Utils.areObjectsEqual(newState, this.state)) {
this.setState(newState);
}
}
@@ -120,7 +128,7 @@ export default class RhsThread extends React.Component {
}
var newState = this.getStateFromStores();
if (!Utils.areObjectsEqual(newState, this.state)) {
if (this.mounted && !Utils.areObjectsEqual(newState, this.state)) {
this.setState(newState);
}
}

Просмотреть файл

@@ -506,9 +506,9 @@ export default class Sidebar extends React.Component {
link.rel = 'shortcut icon';
link.id = 'favicon';
if (this.badgesActive) {
link.href = '/static/images/redfavicon.ico';
link.href = '/static/images/favicon/redfavicon-16x16.png';
} else {
link.href = '/static/images/favicon.ico';
link.href = '/static/images/favicon/favicon-16x16.png';
}
var head = document.getElementsByTagName('head')[0];
var oldLink = document.getElementById('favicon');

Просмотреть файл

@@ -5,6 +5,7 @@ import * as Utils from '../utils/utils.jsx';
import * as client from '../utils/client.jsx';
import UserStore from '../stores/user_store.jsx';
import BrowserStore from '../stores/browser_store.jsx';
import Constants from '../utils/constants.jsx';
export default class SignupUserComplete extends React.Component {
constructor(props) {
@@ -51,7 +52,7 @@ export default class SignupUserComplete extends React.Component {
return;
} else if (usernameError) {
this.setState({
nameError: 'Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols \'.\', \'-\' and \'_\'.',
nameError: 'Username must begin with a letter, and contain between ' + Constants.MIN_USERNAME_LENGTH + ' to ' + Constants.MAX_USERNAME_LENGTH + ' lowercase characters made up of numbers, letters, and the symbols \'.\', \'-\' and \'_\'.',
emailError: '',
passwordError: '',
serverError: ''
@@ -60,8 +61,8 @@ export default class SignupUserComplete extends React.Component {
}
const providedPassword = ReactDOM.findDOMNode(this.refs.password).value.trim();
if (!providedPassword || providedPassword.length < 5) {
this.setState({nameError: '', emailError: '', passwordError: 'Please enter at least 5 characters', serverError: ''});
if (!providedPassword || providedPassword.length < Constants.MIN_PASSWORD_LENGTH) {
this.setState({nameError: '', emailError: '', passwordError: 'Please enter at least ' + Constants.MIN_PASSWORD_LENGTH + ' characters', serverError: ''});
return;
}
@@ -111,7 +112,7 @@ export default class SignupUserComplete extends React.Component {
client.track('signup', 'signup_user_01_welcome');
if (this.state.wizard === 'finished') {
return <div>You've already completed the signup process for this invitation or this invitation has expired.</div>;
return <div>{"You've already completed the signup process for this invitation or this invitation has expired."}</div>;
}
// set up error labels
@@ -123,9 +124,11 @@ export default class SignupUserComplete extends React.Component {
}
var nameError = null;
var nameHelpText = <span className='help-block'>{'Username must begin with a letter, and contain between ' + Constants.MIN_USERNAME_LENGTH + ' to ' + Constants.MAX_USERNAME_LENGTH + " lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'"}</span>;
var nameDivStyle = 'form-group';
if (this.state.nameError) {
nameError = <label className='control-label'>{this.state.nameError}</label>;
nameHelpText = '';
nameDivStyle += ' has-error';
}
@@ -148,7 +151,7 @@ export default class SignupUserComplete extends React.Component {
// set up the email entry and hide it if an email was provided
var yourEmailIs = '';
if (this.state.user.email) {
yourEmailIs = <span>Your email address is <strong>{this.state.user.email}</strong>. You'll use this address to sign in to {global.window.mm_config.SiteName}.</span>;
yourEmailIs = <span>{'Your email address is '}<strong>{this.state.user.email}</strong>{". You'll use this address to sign in to " + global.window.mm_config.SiteName + '.'}</span>;
}
var emailContainerStyle = 'margin--extra';
@@ -158,7 +161,7 @@ export default class SignupUserComplete extends React.Component {
var email = (
<div className={emailContainerStyle}>
<h5><strong>What's your email address?</strong></h5>
<h5><strong>{"What's your email address?"}</strong></h5>
<div className={emailDivStyle}>
<input
type='email'
@@ -208,7 +211,7 @@ export default class SignupUserComplete extends React.Component {
{email}
{yourEmailIs}
<div className='margin--extra'>
<h5><strong>Choose your username</strong></h5>
<h5><strong>{'Choose your username'}</strong></h5>
<div className={nameDivStyle}>
<input
type='text'
@@ -219,11 +222,11 @@ export default class SignupUserComplete extends React.Component {
spellCheck='false'
/>
{nameError}
<span className='help-block'>Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'</span>
{nameHelpText}
</div>
</div>
<div className='margin--extra'>
<h5><strong>Choose your password</strong></h5>
<h5><strong>{'Choose your password'}</strong></h5>
<div className={passwordDivStyle}>
<input
type='password'
@@ -243,7 +246,7 @@ export default class SignupUserComplete extends React.Component {
onClick={this.handleSubmit}
className='btn-primary btn'
>
Create Account
{'Create Account'}
</button>
</p>
</div>
@@ -255,7 +258,7 @@ export default class SignupUserComplete extends React.Component {
<div>
{signupMessage}
<div className='or__container'>
<span>or</span>
<span>{'or'}</span>
</div>
</div>
);
@@ -268,10 +271,10 @@ export default class SignupUserComplete extends React.Component {
className='signup-team-logo'
src='/static/images/logo.png'
/>
<h5 className='margin--less'>Welcome to:</h5>
<h5 className='margin--less'>{'Welcome to:'}</h5>
<h2 className='signup-team__name'>{this.props.teamDisplayName}</h2>
<h2 className='signup-team__subdomain'>on {global.window.mm_config.SiteName}</h2>
<h4 className='color--light'>Let's create your account</h4>
<h2 className='signup-team__subdomain'>{'on ' + global.window.mm_config.SiteName}</h2>
<h4 className='color--light'>{"Let's create your account"}</h4>
{signupMessage}
{emailSignup}
{serverError}

Просмотреть файл

@@ -66,7 +66,7 @@ export default class GeneralTab extends React.Component {
handleTeamListingRadio(listing) {
if (global.window.mm_config.EnableTeamListing !== 'true' && listing) {
this.setState({clientError: 'Team directory has been disabled. Please ask a system admin to enable it.'});
this.setState({clientError: 'Team Directory has been disabled. Please ask a System Admin to enable the Team Directory in the System Console team settings.'});
} else {
this.setState({allow_team_listing: listing});
}

Просмотреть файл

@@ -4,6 +4,7 @@
import * as Client from '../utils/client.jsx';
import BrowserStore from '../stores/browser_store.jsx';
import UserStore from '../stores/user_store.jsx';
import Constants from '../utils/constants.jsx';
export default class TeamSignupPasswordPage extends React.Component {
constructor(props) {
@@ -23,8 +24,8 @@ export default class TeamSignupPasswordPage extends React.Component {
e.preventDefault();
var password = ReactDOM.findDOMNode(this.refs.password).value.trim();
if (!password || password.length < 5) {
this.setState({passwordError: 'Please enter at least 5 characters'});
if (!password || password.length < Constants.MIN_PASSWORD_LENGTH) {
this.setState({passwordError: 'Please enter at least ' + Constants.MIN_PASSWORD_LENGTH + ' characters'});
return;
}
@@ -92,15 +93,15 @@ export default class TeamSignupPasswordPage extends React.Component {
className='signup-team-logo'
src='/static/images/logo.png'
/>
<h2 className='margin--less'>Your password</h2>
<h5 className='color--light'>Select a password that you'll use to login with your email address:</h5>
<h2 className='margin--less'>{'Your password'}</h2>
<h5 className='color--light'>{"Select a password that you'll use to login with your email address:"}</h5>
<div className='inner__content margin--extra'>
<h5><strong>Email</strong></h5>
<h5><strong>{'Email'}</strong></h5>
<div className='block--gray form-group'>{this.props.state.team.email}</div>
<div className={passwordDivStyle}>
<div className='row'>
<div className='col-sm-11'>
<h5><strong>Choose your password</strong></h5>
<h5><strong>{'Choose your password'}</strong></h5>
<input
autoFocus={true}
type='password'
@@ -110,7 +111,7 @@ export default class TeamSignupPasswordPage extends React.Component {
maxLength='128'
spellCheck='false'
/>
<span className='color--light help-block'>Passwords must contain 5 to 50 characters. Your password will be strongest if it contains a mix of symbols, numbers, and upper and lowercase characters.</span>
<span className='color--light help-block'>{'Passwords must contain ' + Constants.MIN_PASSWORD_LENGTH + ' to ' + Constants.MAX_PASSWORD_LENGTH + ' characters. Your password will be strongest if it contains a mix of symbols, numbers, and upper and lowercase characters.'}</span>
</div>
</div>
{passwordError}
@@ -125,7 +126,7 @@ export default class TeamSignupPasswordPage extends React.Component {
data-loading-text={'<span class=\'glyphicon glyphicon-refresh glyphicon-refresh-animate\'></span> Creating team...'}
onClick={this.submitNext}
>
Finish
{'Finish'}
</button>
</div>
<p>By proceeding to create your account and use {global.window.mm_config.SiteName}, you agree to our <a href='/static/help/terms.html'>Terms of Service</a> and <a href='/static/help/privacy.html'>Privacy Policy</a>. If you do not agree, you cannot use {global.window.mm_config.SiteName}.</p>
@@ -134,7 +135,7 @@ export default class TeamSignupPasswordPage extends React.Component {
href='#'
onClick={this.submitBack}
>
Back to previous step
{'Back to previous step'}
</a>
</div>
</form>

Просмотреть файл

@@ -3,6 +3,7 @@
import * as Utils from '../utils/utils.jsx';
import * as Client from '../utils/client.jsx';
import Constants from '../utils/constants.jsx';
export default class TeamSignupUsernamePage extends React.Component {
constructor(props) {
@@ -33,7 +34,7 @@ export default class TeamSignupUsernamePage extends React.Component {
this.setState({nameError: 'This username is reserved, please choose a new one.'});
return;
} else if (usernameError) {
this.setState({nameError: 'Username must begin with a letter, and contain 3 to 15 characters in total, which may be numbers, lowercase letters, or any of the symbols \'.\', \'-\', or \'_\''});
this.setState({nameError: 'Username must begin with a letter, and contain between ' + Constants.MIN_USERNAME_LENGTH + ' to ' + Constants.MAX_USERNAME_LENGTH + ' characters in total, which may be numbers, lowercase letters, or any of the symbols \'.\', \'-\', or \'_\''});
return;
}
@@ -45,9 +46,11 @@ export default class TeamSignupUsernamePage extends React.Component {
Client.track('signup', 'signup_team_06_username');
var nameError = null;
var nameHelpText = <span className='color--light help-block'>{'Usernames must begin with a letter and contain between ' + Constants.MIN_USERNAME_LENGTH + ' to ' + Constants.MAX_USERNAME_LENGTH + " characters made up of lowercase letters, numbers, and the symbols '.', '-' and '_'"}</span>;
var nameDivClass = 'form-group';
if (this.state.nameError) {
nameError = <label className='control-label'>{this.state.nameError}</label>;
nameHelpText = '';
nameDivClass += ' has-error';
}
@@ -58,13 +61,13 @@ export default class TeamSignupUsernamePage extends React.Component {
className='signup-team-logo'
src='/static/images/logo.png'
/>
<h2 className='margin--less'>Your username</h2>
<h2 className='margin--less'>{'Your username'}</h2>
<h5 className='color--light'>{'Select a memorable username that makes it easy for teammates to identify you:'}</h5>
<div className='inner__content margin--extra'>
<div className={nameDivClass}>
<div className='row'>
<div className='col-sm-11'>
<h5><strong>Choose your username</strong></h5>
<h5><strong>{'Choose your username'}</strong></h5>
<input
autoFocus={true}
type='text'
@@ -75,7 +78,7 @@ export default class TeamSignupUsernamePage extends React.Component {
maxLength='128'
spellCheck='false'
/>
<span className='color--light help-block'>Usernames must begin with a letter and contain 3 to 15 characters made up of lowercase letters, numbers, and the symbols '.', '-' and '_'</span>
{nameHelpText}
</div>
</div>
{nameError}
@@ -86,7 +89,7 @@ export default class TeamSignupUsernamePage extends React.Component {
className='btn btn-primary margin--extra'
onClick={this.submitNext}
>
Next
{'Next'}
<i className='glyphicon glyphicon-chevron-right'></i>
</button>
<div className='margin--extra'>
@@ -94,7 +97,7 @@ export default class TeamSignupUsernamePage extends React.Component {
href='#'
onClick={this.submitBack}
>
Back to previous step
{'Back to previous step'}
</a>
</div>
</form>

Просмотреть файл

@@ -22,8 +22,6 @@ export default class Textbox extends React.Component {
this.handleKeyPress = this.handleKeyPress.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.resize = this.resize.bind(this);
this.handleFocus = this.handleFocus.bind(this);
this.handleBlur = this.handleBlur.bind(this);
this.showPreview = this.showPreview.bind(this);
this.state = {
@@ -81,54 +79,46 @@ export default class Textbox extends React.Component {
}
resize() {
const e = this.refs.message.getTextbox();
const w = ReactDOM.findDOMNode(this.refs.wrapper);
const textbox = this.refs.message.getTextbox();
const $textbox = $(textbox);
const $wrapper = $(ReactDOM.findDOMNode(this.refs.wrapper));
const prevHeight = $(e).height();
const padding = parseInt($textbox.css('padding-bottom'), 10) + parseInt($textbox.css('padding-top'), 10);
const borders = parseInt($textbox.css('border-bottom-width'), 10) + parseInt($textbox.css('border-top-width'), 10);
const maxHeight = parseInt($textbox.css('max-height'), 10) - borders;
const lht = parseInt($(e).css('lineHeight'), 10);
const lines = e.scrollHeight / lht;
let mod = 15;
const prevHeight = $textbox.height();
if (lines < 2.5 || this.props.messageText === '') {
mod = 30;
}
// set the height to auto and remove the scrollbar so we can get the actual size of the contents
$textbox.css('height', 'auto').css('overflow-y', 'hidden');
if (e.scrollHeight - mod < 167) {
$(e).css({height: 'auto', 'overflow-y': 'hidden'}).height(e.scrollHeight - mod);
$(w).css({height: 'auto'}).height(e.scrollHeight + 2);
$(w).closest('.post-body__cell').removeClass('scroll');
if (this.state.preview) {
$(ReactDOM.findDOMNode(this.refs.preview)).css({height: 'auto', 'overflow-y': 'auto'}).height(e.scrollHeight - mod);
}
let height = textbox.scrollHeight - padding;
if (height + padding > maxHeight) {
height = maxHeight - padding;
// turn scrollbar on and move over attachment icon to compensate for that
$textbox.css('overflow-y', 'scroll');
$wrapper.closest('.post-body__cell').addClass('scroll');
} else {
$(e).css({height: 'auto', 'overflow-y': 'scroll'}).height(167 - mod);
$(w).css({height: 'auto'}).height(163);
$(w).closest('.post-body__cell').addClass('scroll');
if (this.state.preview) {
$(ReactDOM.findDOMNode(this.refs.preview)).css({height: 'auto', 'overflow-y': 'scroll'}).height(163);
}
$wrapper.closest('.post-body__cell').removeClass('scroll');
}
if (prevHeight !== $(e).height() && this.props.onHeightChange) {
// set the textarea to be the proper height
$textbox.height(height);
// set the wrapper height to match the height of the textbox including padding and borders
$wrapper.height(height + padding + borders);
if (this.state.preview) {
$(ReactDOM.findDOMNode(this.refs.preview)).height(height + borders);
}
if (height !== prevHeight && this.props.onHeightChange) {
this.props.onHeightChange();
}
}
handleFocus() {
const elm = this.refs.message.getTextbox();
if (elm.title === elm.value) {
elm.value = '';
}
}
handleBlur() {
const elm = this.refs.message.getTextbox();
if (elm.value === '') {
elm.value = elm.title;
}
}
showPreview(e) {
e.preventDefault();
e.target.blur();
@@ -178,9 +168,6 @@ export default class Textbox extends React.Component {
onUserInput={this.props.onUserInput}
onKeyPress={this.handleKeyPress}
onKeyDown={this.handleKeyDown}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
onPaste={this.handlePaste}
style={{visibility: this.state.preview ? 'hidden' : 'visible'}}
listComponent={SuggestionList}
providers={this.suggestionProviders}

Просмотреть файл

@@ -10,7 +10,7 @@ export default class UnreadChannelIndicator extends React.Component {
render() {
let displayValue = 'none';
if (this.props.show) {
displayValue = 'initial';
displayValue = 'block';
}
return (
<div

Просмотреть файл

@@ -0,0 +1,101 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
import * as Client from '../../utils/client.jsx';
import * as Utils from '../../utils/utils.jsx';
export default class ManageLanguage extends React.Component {
constructor(props) {
super(props);
this.setupInitialState = this.setupInitialState.bind(this);
this.setLanguage = this.setLanguage.bind(this);
this.changeLanguage = this.changeLanguage.bind(this);
this.submitUser = this.submitUser.bind(this);
this.state = this.setupInitialState(props);
}
setupInitialState(props) {
var user = props.user;
return {
languages: Utils.languages(),
locale: user.locale
};
}
setLanguage(e) {
this.setState({locale: e.target.value});
}
changeLanguage(e) {
e.preventDefault();
var user = this.props.user;
var locale = this.state.locale;
user.locale = locale;
this.submitUser(user);
}
submitUser(user) {
Client.updateUser(user,
() => {
window.location.reload(true);
},
(err) => {
let serverError;
if (err.message) {
serverError = err.message;
} else {
serverError = err;
}
this.setState({serverError});
}
);
}
render() {
let serverError;
if (this.state.serverError) {
serverError = <label className='has-error'>{this.state.serverError}</label>;
}
const options = [];
this.state.languages.forEach((lang) => {
options.push(
<option
key={lang.value}
value={lang.value}
>
{lang.name}
</option>);
});
return (
<div key='changeLanguage'>
<br/>
<label className='control-label'>{'Change interface language'}</label>
<div className='padding-top'>
<select
ref='language'
className='form-control'
value={this.state.locale}
onChange={this.setLanguage}
>
{options}
</select>
{serverError}
<div className='padding-top'>
<a
className={'btn btn-sm btn-primary'}
href='#'
onClick={this.changeLanguage}
>
{'Set language'}
</a>
</div>
</div>
</div>
);
}
}
ManageLanguage.propTypes = {
user: React.PropTypes.object
};

Просмотреть файл

@@ -5,7 +5,9 @@ import {savePreferences} from '../../utils/client.jsx';
import SettingItemMin from '../setting_item_min.jsx';
import SettingItemMax from '../setting_item_max.jsx';
import Constants from '../../utils/constants.jsx';
const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES;
import PreferenceStore from '../../stores/preference_store.jsx';
import ManageLanguages from './manage_languages.jsx';
import * as Utils from '../../utils/utils.jsx';
function getDisplayStateFromStores() {
@@ -78,6 +80,7 @@ export default class UserSettingsDisplay extends React.Component {
let clockSection;
let nameFormatSection;
let fontSection;
let languagesSection;
if (this.props.activeSection === 'clock') {
const clockFormat = [false, false];
@@ -292,6 +295,48 @@ export default class UserSettingsDisplay extends React.Component {
);
}
if (Utils.isFeatureEnabled(PreReleaseFeatures.LOC_PREVIEW)) {
if (this.props.activeSection === 'languages') {
var inputs = [];
inputs.push(
<ManageLanguages
user={this.props.user}
key='languages-ui'
/>
);
languagesSection = (
<SettingItemMax
title={'Language'}
width='medium'
inputs={inputs}
updateSection={(e) => {
this.updateSection('');
e.preventDefault();
}}
/>
);
} else {
var locale = 'English';
Utils.languages().forEach((l) => {
if (l.value === this.props.user.locale) {
locale = l.name;
}
});
languagesSection = (
<SettingItemMin
title={'Language'}
width='medium'
describe={locale}
updateSection={() => {
this.updateSection('languages');
}}
/>
);
}
}
return (
<div>
<div className='modal-header'>
@@ -324,6 +369,7 @@ export default class UserSettingsDisplay extends React.Component {
<div className='divider-dark'/>
{nameFormatSection}
<div className='divider-dark'/>
{languagesSection}
</div>
</div>
);

Просмотреть файл

@@ -47,7 +47,7 @@ export default class UserSettingsGeneralTab extends React.Component {
this.setState({clientError: 'This username is reserved, please choose a new one.'});
return;
} else if (usernameError) {
this.setState({clientError: "Username must begin with a letter, and contain between 3 to 15 lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'."});
this.setState({clientError: 'Username must begin with a letter, and contain between ' + Constants.MIN_USERNAME_LENGTH + ' to ' + Constants.MAX_USERNAME_LENGTH + " lowercase characters made up of numbers, letters, and the symbols '.', '-' and '_'."});
return;
}
@@ -493,7 +493,7 @@ export default class UserSettingsGeneralTab extends React.Component {
);
submit = this.submitEmail;
} else {
} else if (this.props.user.auth_service === Constants.GITLAB_SERVICE) {
inputs.push(
<div
key='oauthEmailInfo'
@@ -531,7 +531,7 @@ export default class UserSettingsGeneralTab extends React.Component {
} else {
describe = UserStore.getCurrentUser().email;
}
} else {
} else if (this.props.user.auth_service === Constants.GITLAB_SERVICE) {
describe = 'Log in done through GitLab';
}

Просмотреть файл

@@ -48,8 +48,8 @@ export default class SecurityTab extends React.Component {
return;
}
if (newPassword.length < 5) {
this.setState({passwordError: 'New passwords must be at least 5 characters', serverError: ''});
if (newPassword.length < Constants.MIN_PASSWORD_LENGTH) {
this.setState({passwordError: 'New passwords must be at least ' + Constants.MIN_PASSWORD_LENGTH + ' characters', serverError: ''});
return;
}
@@ -337,7 +337,7 @@ export default class SecurityTab extends React.Component {
className='security-links theme'
dialogType={AccessHistoryModal}
>
<i className='fa fa-clock-o'></i>View Access History
<i className='fa fa-clock-o'></i>{'View Access History'}
</ToggleModalButton>
<b> </b>
<ToggleModalButton

Просмотреть файл

@@ -211,7 +211,7 @@ export default class ViewImageModal extends React.Component {
}
const filename = this.props.filenames[this.state.imgId];
const fileUrl = Utils.getFileUrl(filename);
const fileUrl = Utils.getFileUrl(filename, true);
var content;
if (this.state.loaded[this.state.imgId]) {
@@ -377,6 +377,7 @@ function ImagePreview({filename, fileUrl, fileInfo, maxHeight}) {
<a
href={fileUrl}
target='_blank'
download={true}
>
<img
style={{maxHeight}}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше