mirror of
https://github.com/astaxie/beego.git
synced 2024-11-22 12:30:54 +00:00
Merge branch 'astaxie/develop' into iniSaveErrorFix
# Conflicts: # config/ini_test.go
This commit is contained in:
commit
cf055c9db2
@ -106,3 +106,41 @@ func NewConfigData(adapterName string, data []byte) (Configer, error) {
|
|||||||
}
|
}
|
||||||
return adapter.ParseData(data)
|
return adapter.ParseData(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseBool returns the boolean value represented by the string.
|
||||||
|
//
|
||||||
|
// It accepts 1, 1.0, t, T, TRUE, true, True, YES, yes, Yes,Y, y, ON, on, On,
|
||||||
|
// 0, 0.0, f, F, FALSE, false, False, NO, no, No, N,n, OFF, off, Off.
|
||||||
|
// Any other value returns an error.
|
||||||
|
func ParseBool(val interface{}) (value bool, err error) {
|
||||||
|
|
||||||
|
if val != nil {
|
||||||
|
switch v := val.(type) {
|
||||||
|
case bool:
|
||||||
|
return v, nil
|
||||||
|
case string:
|
||||||
|
switch v {
|
||||||
|
case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "Y", "y", "ON", "on", "On":
|
||||||
|
return true, nil
|
||||||
|
case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "N", "n", "OFF", "off", "Off":
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
case int8, int32, int64:
|
||||||
|
strV := fmt.Sprintf("%s", v)
|
||||||
|
if strV == "1" {
|
||||||
|
return true, nil
|
||||||
|
} else if strV == "0" {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
if v == 1 {
|
||||||
|
return true, nil
|
||||||
|
} else if v == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("parsing %q: invalid syntax", val)
|
||||||
|
} else {
|
||||||
|
return false, fmt.Errorf("parsing <nil>: invalid syntax")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -82,7 +82,7 @@ func (c *fakeConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeConfigContainer) Bool(key string) (bool, error) {
|
func (c *fakeConfigContainer) Bool(key string) (bool, error) {
|
||||||
return strconv.ParseBool(c.getData(key))
|
return ParseBool(c.getData(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeConfigContainer) DefaultBool(key string, defaultval bool) bool {
|
func (c *fakeConfigContainer) DefaultBool(key string, defaultval bool) bool {
|
||||||
|
@ -195,7 +195,7 @@ type IniConfigContainer struct {
|
|||||||
|
|
||||||
// Bool returns the boolean value for a given key.
|
// Bool returns the boolean value for a given key.
|
||||||
func (c *IniConfigContainer) Bool(key string) (bool, error) {
|
func (c *IniConfigContainer) Bool(key string) (bool, error) {
|
||||||
return strconv.ParseBool(c.getdata(key))
|
return ParseBool(c.getdata(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultBool returns the boolean value for a given key.
|
// DefaultBool returns the boolean value for a given key.
|
||||||
|
@ -15,12 +15,17 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var inicontext = `
|
func TestIni(t *testing.T) {
|
||||||
|
|
||||||
|
var (
|
||||||
|
inicontext = `
|
||||||
;comment one
|
;comment one
|
||||||
#comment two
|
#comment two
|
||||||
appname = beeapi
|
appname = beeapi
|
||||||
@ -30,6 +35,13 @@ PI = 3.1415976
|
|||||||
runmode = "dev"
|
runmode = "dev"
|
||||||
autorender = false
|
autorender = false
|
||||||
copyrequestbody = true
|
copyrequestbody = true
|
||||||
|
session= on
|
||||||
|
cookieon= off
|
||||||
|
newreg = OFF
|
||||||
|
needlogin = ON
|
||||||
|
enableSession = Y
|
||||||
|
enableCookie = N
|
||||||
|
flag = 1
|
||||||
[demo]
|
[demo]
|
||||||
key1="asta"
|
key1="asta"
|
||||||
key2 = "xie"
|
key2 = "xie"
|
||||||
@ -37,7 +49,31 @@ CaseInsensitive = true
|
|||||||
peers = one;two;three
|
peers = one;two;three
|
||||||
`
|
`
|
||||||
|
|
||||||
func TestIni(t *testing.T) {
|
keyValue = map[string]interface{}{
|
||||||
|
"appname": "beeapi",
|
||||||
|
"httpport": 8080,
|
||||||
|
"mysqlport": int64(3600),
|
||||||
|
"pi": 3.1415976,
|
||||||
|
"runmode": "dev",
|
||||||
|
"autorender": false,
|
||||||
|
"copyrequestbody": true,
|
||||||
|
"session": true,
|
||||||
|
"cookieon": false,
|
||||||
|
"newreg": false,
|
||||||
|
"needlogin": true,
|
||||||
|
"enableSession": true,
|
||||||
|
"enableCookie": false,
|
||||||
|
"flag": true,
|
||||||
|
"demo::key1": "asta",
|
||||||
|
"demo::key2": "xie",
|
||||||
|
"demo::CaseInsensitive": true,
|
||||||
|
"demo::peers": []string{"one", "two", "three"},
|
||||||
|
"null": "",
|
||||||
|
"demo2::key1": "",
|
||||||
|
"error": "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
f, err := os.Create("testini.conf")
|
f, err := os.Create("testini.conf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -53,31 +89,31 @@ func TestIni(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if iniconf.String("appname") != "beeapi" {
|
for k, v := range keyValue {
|
||||||
t.Fatal("appname not equal to beeapi")
|
var err error
|
||||||
}
|
var value interface{}
|
||||||
if port, err := iniconf.Int("httpport"); err != nil || port != 8080 {
|
switch v.(type) {
|
||||||
t.Error(port)
|
case int:
|
||||||
t.Fatal(err)
|
value, err = iniconf.Int(k)
|
||||||
}
|
case int64:
|
||||||
if port, err := iniconf.Int64("mysqlport"); err != nil || port != 3600 {
|
value, err = iniconf.Int64(k)
|
||||||
t.Error(port)
|
case float64:
|
||||||
t.Fatal(err)
|
value, err = iniconf.Float(k)
|
||||||
}
|
case bool:
|
||||||
if pi, err := iniconf.Float("PI"); err != nil || pi != 3.1415976 {
|
value, err = iniconf.Bool(k)
|
||||||
t.Error(pi)
|
case []string:
|
||||||
t.Fatal(err)
|
value = iniconf.Strings(k)
|
||||||
}
|
case string:
|
||||||
if iniconf.String("runmode") != "dev" {
|
value = iniconf.String(k)
|
||||||
t.Fatal("runmode not equal to dev")
|
default:
|
||||||
}
|
value, err = iniconf.DIY(k)
|
||||||
if v, err := iniconf.Bool("autorender"); err != nil || v != false {
|
}
|
||||||
t.Error(v)
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatalf("get key %q value fail,err %s", k, err)
|
||||||
}
|
} else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) {
|
||||||
if v, err := iniconf.Bool("copyrequestbody"); err != nil || v != true {
|
t.Fatalf("get key %q value, want %v got %v .", k, v, value)
|
||||||
t.Error(v)
|
}
|
||||||
t.Fatal(err)
|
|
||||||
}
|
}
|
||||||
if err = iniconf.Set("name", "astaxie"); err != nil {
|
if err = iniconf.Set("name", "astaxie"); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -85,21 +121,6 @@ func TestIni(t *testing.T) {
|
|||||||
if iniconf.String("name") != "astaxie" {
|
if iniconf.String("name") != "astaxie" {
|
||||||
t.Fatal("get name error")
|
t.Fatal("get name error")
|
||||||
}
|
}
|
||||||
if iniconf.String("demo::key1") != "asta" {
|
|
||||||
t.Fatal("get demo.key1 error")
|
|
||||||
}
|
|
||||||
if iniconf.String("demo::key2") != "xie" {
|
|
||||||
t.Fatal("get demo.key2 error")
|
|
||||||
}
|
|
||||||
if v, err := iniconf.Bool("demo::caseinsensitive"); err != nil || v != true {
|
|
||||||
t.Fatal("get demo.caseinsensitive error")
|
|
||||||
}
|
|
||||||
|
|
||||||
if data := iniconf.Strings("demo::peers"); len(data) != 3 {
|
|
||||||
t.Fatal("get strings error", data)
|
|
||||||
} else if data[0] != "one" {
|
|
||||||
t.Fatal("get first params error not equat to one")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +142,8 @@ httpport = 8080
|
|||||||
name = mysql
|
name = mysql
|
||||||
`
|
`
|
||||||
|
|
||||||
saveResult = `app=app
|
saveResult = `
|
||||||
|
app=app
|
||||||
#comment one
|
#comment one
|
||||||
#comment two
|
#comment two
|
||||||
# comment three
|
# comment three
|
||||||
@ -134,7 +156,6 @@ httpport=8080
|
|||||||
# db type name
|
# db type name
|
||||||
# suport mysql,sqlserver
|
# suport mysql,sqlserver
|
||||||
name=mysql
|
name=mysql
|
||||||
|
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
cfg, err := NewConfigData("ini", []byte(inicontext))
|
cfg, err := NewConfigData("ini", []byte(inicontext))
|
||||||
@ -149,7 +170,14 @@ name=mysql
|
|||||||
|
|
||||||
if data, err := ioutil.ReadFile(name); err != nil {
|
if data, err := ioutil.ReadFile(name); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if string(data) != saveResult {
|
} else {
|
||||||
t.Fatal("different after save ini config file.")
|
cfgData := string(data)
|
||||||
|
datas := strings.Split(saveResult, "\n")
|
||||||
|
for _, line := range datas {
|
||||||
|
if strings.Contains(cfgData, line+"\n") == false {
|
||||||
|
t.Fatalf("different after save ini config file. need contains %q", line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ package config
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -70,12 +71,9 @@ type JSONConfigContainer struct {
|
|||||||
func (c *JSONConfigContainer) Bool(key string) (bool, error) {
|
func (c *JSONConfigContainer) Bool(key string) (bool, error) {
|
||||||
val := c.getData(key)
|
val := c.getData(key)
|
||||||
if val != nil {
|
if val != nil {
|
||||||
if v, ok := val.(bool); ok {
|
return ParseBool(val)
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
return false, errors.New("not bool value")
|
|
||||||
}
|
}
|
||||||
return false, errors.New("not exist key:" + key)
|
return false, fmt.Errorf("not exist key: %q", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultBool return the bool value if has no error
|
// DefaultBool return the bool value if has no error
|
||||||
|
@ -15,34 +15,14 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var jsoncontext = `{
|
func TestJsonStartsWithArray(t *testing.T) {
|
||||||
"appname": "beeapi",
|
|
||||||
"testnames": "foo;bar",
|
|
||||||
"httpport": 8080,
|
|
||||||
"mysqlport": 3600,
|
|
||||||
"PI": 3.1415976,
|
|
||||||
"runmode": "dev",
|
|
||||||
"autorender": false,
|
|
||||||
"copyrequestbody": true,
|
|
||||||
"database": {
|
|
||||||
"host": "host",
|
|
||||||
"port": "port",
|
|
||||||
"database": "database",
|
|
||||||
"username": "username",
|
|
||||||
"password": "password",
|
|
||||||
"conns":{
|
|
||||||
"maxconnection":12,
|
|
||||||
"autoconnect":true,
|
|
||||||
"connectioninfo":"info"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
|
|
||||||
var jsoncontextwitharray = `[
|
const jsoncontextwitharray = `[
|
||||||
{
|
{
|
||||||
"url": "user",
|
"url": "user",
|
||||||
"serviceAPI": "http://www.test.com/user"
|
"serviceAPI": "http://www.test.com/user"
|
||||||
@ -52,8 +32,6 @@ var jsoncontextwitharray = `[
|
|||||||
"serviceAPI": "http://www.test.com/employee"
|
"serviceAPI": "http://www.test.com/employee"
|
||||||
}
|
}
|
||||||
]`
|
]`
|
||||||
|
|
||||||
func TestJsonStartsWithArray(t *testing.T) {
|
|
||||||
f, err := os.Create("testjsonWithArray.conf")
|
f, err := os.Create("testjsonWithArray.conf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -90,6 +68,64 @@ func TestJsonStartsWithArray(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestJson(t *testing.T) {
|
func TestJson(t *testing.T) {
|
||||||
|
|
||||||
|
var (
|
||||||
|
jsoncontext = `{
|
||||||
|
"appname": "beeapi",
|
||||||
|
"testnames": "foo;bar",
|
||||||
|
"httpport": 8080,
|
||||||
|
"mysqlport": 3600,
|
||||||
|
"PI": 3.1415976,
|
||||||
|
"runmode": "dev",
|
||||||
|
"autorender": false,
|
||||||
|
"copyrequestbody": true,
|
||||||
|
"session": "on",
|
||||||
|
"cookieon": "off",
|
||||||
|
"newreg": "OFF",
|
||||||
|
"needlogin": "ON",
|
||||||
|
"enableSession": "Y",
|
||||||
|
"enableCookie": "N",
|
||||||
|
"flag": 1,
|
||||||
|
"database": {
|
||||||
|
"host": "host",
|
||||||
|
"port": "port",
|
||||||
|
"database": "database",
|
||||||
|
"username": "username",
|
||||||
|
"password": "password",
|
||||||
|
"conns":{
|
||||||
|
"maxconnection":12,
|
||||||
|
"autoconnect":true,
|
||||||
|
"connectioninfo":"info"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
keyValue = map[string]interface{}{
|
||||||
|
"appname": "beeapi",
|
||||||
|
"testnames": []string{"foo", "bar"},
|
||||||
|
"httpport": 8080,
|
||||||
|
"mysqlport": int64(3600),
|
||||||
|
"PI": 3.1415976,
|
||||||
|
"runmode": "dev",
|
||||||
|
"autorender": false,
|
||||||
|
"copyrequestbody": true,
|
||||||
|
"session": true,
|
||||||
|
"cookieon": false,
|
||||||
|
"newreg": false,
|
||||||
|
"needlogin": true,
|
||||||
|
"enableSession": true,
|
||||||
|
"enableCookie": false,
|
||||||
|
"flag": true,
|
||||||
|
"database::host": "host",
|
||||||
|
"database::port": "port",
|
||||||
|
"database::database": "database",
|
||||||
|
"database::password": "password",
|
||||||
|
"database::conns::maxconnection": 12,
|
||||||
|
"database::conns::autoconnect": true,
|
||||||
|
"database::conns::connectioninfo": "info",
|
||||||
|
"unknown": "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
f, err := os.Create("testjson.conf")
|
f, err := os.Create("testjson.conf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -105,37 +141,32 @@ func TestJson(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if jsonconf.String("appname") != "beeapi" {
|
|
||||||
t.Fatal("appname not equal to beeapi")
|
for k, v := range keyValue {
|
||||||
}
|
var err error
|
||||||
if port, err := jsonconf.Int("httpport"); err != nil || port != 8080 {
|
var value interface{}
|
||||||
t.Error(port)
|
switch v.(type) {
|
||||||
t.Fatal(err)
|
case int:
|
||||||
}
|
value, err = jsonconf.Int(k)
|
||||||
if port, err := jsonconf.Int64("mysqlport"); err != nil || port != 3600 {
|
case int64:
|
||||||
t.Error(port)
|
value, err = jsonconf.Int64(k)
|
||||||
t.Fatal(err)
|
case float64:
|
||||||
}
|
value, err = jsonconf.Float(k)
|
||||||
if pi, err := jsonconf.Float("PI"); err != nil || pi != 3.1415976 {
|
case bool:
|
||||||
t.Error(pi)
|
value, err = jsonconf.Bool(k)
|
||||||
t.Fatal(err)
|
case []string:
|
||||||
}
|
value = jsonconf.Strings(k)
|
||||||
if jsonconf.String("runmode") != "dev" {
|
case string:
|
||||||
t.Fatal("runmode not equal to dev")
|
value = jsonconf.String(k)
|
||||||
}
|
default:
|
||||||
if v := jsonconf.Strings("unknown"); len(v) > 0 {
|
value, err = jsonconf.DIY(k)
|
||||||
t.Fatal("unknown strings, the length should be 0")
|
}
|
||||||
}
|
if err != nil {
|
||||||
if v := jsonconf.Strings("testnames"); len(v) != 2 {
|
t.Fatalf("get key %q value fatal,%V err %s", k, v, err)
|
||||||
t.Fatal("testnames length should be 2")
|
} else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) {
|
||||||
}
|
t.Fatalf("get key %q value, want %v got %v .", k, v, value)
|
||||||
if v, err := jsonconf.Bool("autorender"); err != nil || v != false {
|
}
|
||||||
t.Error(v)
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if v, err := jsonconf.Bool("copyrequestbody"); err != nil || v != true {
|
|
||||||
t.Error(v)
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
}
|
||||||
if err = jsonconf.Set("name", "astaxie"); err != nil {
|
if err = jsonconf.Set("name", "astaxie"); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -143,15 +174,7 @@ func TestJson(t *testing.T) {
|
|||||||
if jsonconf.String("name") != "astaxie" {
|
if jsonconf.String("name") != "astaxie" {
|
||||||
t.Fatal("get name error")
|
t.Fatal("get name error")
|
||||||
}
|
}
|
||||||
if jsonconf.String("database::host") != "host" {
|
|
||||||
t.Fatal("get database::host error")
|
|
||||||
}
|
|
||||||
if jsonconf.String("database::conns::connectioninfo") != "info" {
|
|
||||||
t.Fatal("get database::conns::connectioninfo error")
|
|
||||||
}
|
|
||||||
if maxconnection, err := jsonconf.Int("database::conns::maxconnection"); err != nil || maxconnection != 12 {
|
|
||||||
t.Fatal("get database::conns::maxconnection error")
|
|
||||||
}
|
|
||||||
if db, err := jsonconf.DIY("database"); err != nil {
|
if db, err := jsonconf.DIY("database"); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if m, ok := db.(map[string]interface{}); !ok {
|
} else if m, ok := db.(map[string]interface{}); !ok {
|
||||||
|
@ -92,7 +92,11 @@ type ConfigContainer struct {
|
|||||||
|
|
||||||
// Bool returns the boolean value for a given key.
|
// Bool returns the boolean value for a given key.
|
||||||
func (c *ConfigContainer) Bool(key string) (bool, error) {
|
func (c *ConfigContainer) Bool(key string) (bool, error) {
|
||||||
return strconv.ParseBool(c.data[key].(string))
|
if v, ok := c.data[key]; ok {
|
||||||
|
return config.ParseBool(v)
|
||||||
|
} else {
|
||||||
|
return false, fmt.Errorf("not exist key: %q", key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultBool return the bool value if has no error
|
// DefaultBool return the bool value if has no error
|
||||||
|
@ -121,10 +121,11 @@ type ConfigContainer struct {
|
|||||||
|
|
||||||
// Bool returns the boolean value for a given key.
|
// Bool returns the boolean value for a given key.
|
||||||
func (c *ConfigContainer) Bool(key string) (bool, error) {
|
func (c *ConfigContainer) Bool(key string) (bool, error) {
|
||||||
if v, ok := c.data[key].(bool); ok {
|
if v, ok := c.data[key]; ok {
|
||||||
return v, nil
|
return config.ParseBool(v)
|
||||||
|
} else {
|
||||||
|
return false, fmt.Errorf("not exist key: %q", key)
|
||||||
}
|
}
|
||||||
return false, errors.New("not bool value")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultBool return the bool value if has no error
|
// DefaultBool return the bool value if has no error
|
||||||
|
6
tree.go
6
tree.go
@ -420,7 +420,11 @@ func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok b
|
|||||||
if len(strs) == 2 {
|
if len(strs) == 2 {
|
||||||
ctx.Input.SetParam(":ext", strs[1])
|
ctx.Input.SetParam(":ext", strs[1])
|
||||||
}
|
}
|
||||||
ctx.Input.SetParam(":path", path.Join(path.Join(wildcardValues[index:len(wildcardValues)-1]...), strs[0]))
|
if index > (len(wildcardValues) - 1) {
|
||||||
|
ctx.Input.SetParam(":path", "")
|
||||||
|
} else {
|
||||||
|
ctx.Input.SetParam(":path", path.Join(path.Join(wildcardValues[index:len(wildcardValues)-1]...), strs[0]))
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match :id
|
// match :id
|
||||||
|
Loading…
Reference in New Issue
Block a user