merging
Этот коммит содержится в:
2
.gitignore
поставляемый
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
сгенерированный
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
44
Godeps/_workspace/src/github.com/cloudfoundry/jibber_jabber/README.md
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,44 @@
|
||||
# Jibber Jabber [](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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
1685
Godeps/_workspace/src/gopkg.in/yaml.v2/emitterc.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
306
Godeps/_workspace/src/gopkg.in/yaml.v2/encode.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
сгенерированный
поставляемый
Обычный файл
1096
Godeps/_workspace/src/gopkg.in/yaml.v2/parserc.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
394
Godeps/_workspace/src/gopkg.in/yaml.v2/readerc.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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
2710
Godeps/_workspace/src/gopkg.in/yaml.v2/scannerc.go
сгенерированный
поставляемый
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
104
Godeps/_workspace/src/gopkg.in/yaml.v2/sorter.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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
сгенерированный
поставляемый
Обычный файл
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
|
||||
|
||||
}
|
||||
10
Makefile
10
Makefile
@@ -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
api/api.go
10
api/api.go
@@ -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
|
||||
|
||||
79
api/file.go
79
api/file.go
@@ -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
|
||||
}
|
||||
|
||||
|
||||
26
api/oauth.go
26
api/oauth.go
@@ -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)
|
||||
|
||||
137
api/post.go
137
api/post.go
@@ -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
|
||||
}
|
||||
|
||||
54
api/team.go
54
api/team.go
@@ -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}}
|
||||
|
||||
180
api/user.go
180
api/user.go
@@ -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 couldn’t 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
Двоичные данные
docker/1.4/Dockerrun.aws.zip
Двоичный файл не отображается.
1330
i18n/en.json
Обычный файл
1330
i18n/en.json
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
1330
i18n/es.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
Обычный файл
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
|
||||
|
||||
101
web/react/components/user_settings/manage_languages.jsx
Обычный файл
101
web/react/components/user_settings/manage_languages.jsx
Обычный файл
@@ -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}}
|
||||
|
||||
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Ссылка в новой задаче
Block a user