Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
B
beego
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
beego
Commits
46aa340b
Commit
46aa340b
authored
Dec 17, 2015
by
astaxie
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1478 from fuxiaohei/develop
clean compliated codes, refactor if sections in app.go
parents
2aa50c24
a06022f7
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
80 additions
and
85 deletions
+80
-85
admin.go
admin.go
+54
-56
app.go
app.go
+26
-29
No files found.
admin.go
View file @
46aa340b
...
@@ -81,7 +81,11 @@ func qpsIndex(rw http.ResponseWriter, r *http.Request) {
...
@@ -81,7 +81,11 @@ func qpsIndex(rw http.ResponseWriter, r *http.Request) {
func
listConf
(
rw
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
func
listConf
(
rw
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
r
.
ParseForm
()
r
.
ParseForm
()
command
:=
r
.
Form
.
Get
(
"command"
)
command
:=
r
.
Form
.
Get
(
"command"
)
if
command
!=
""
{
if
command
==
""
{
rw
.
Write
([]
byte
(
"command not support"
))
return
}
data
:=
make
(
map
[
interface
{}]
interface
{})
data
:=
make
(
map
[
interface
{}]
interface
{})
switch
command
{
switch
command
{
case
"conf"
:
case
"conf"
:
...
@@ -94,17 +98,17 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
...
@@ -94,17 +98,17 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
tmpl
.
Execute
(
rw
,
data
)
tmpl
.
Execute
(
rw
,
data
)
case
"router"
:
case
"router"
:
content
:=
make
(
map
[
string
]
interface
{})
var
(
content
=
map
[
string
]
interface
{}{
var
fields
=
[]
string
{
"Fields"
:
[]
string
{
fmt
.
Sprintf
(
"Router Pattern"
)
,
"Router Pattern"
,
fmt
.
Sprintf
(
"Methods"
)
,
"Methods"
,
fmt
.
Sprintf
(
"Controller"
)
,
"Controller"
,
}
},
content
[
"Fields"
]
=
fields
}
methods
=
[]
string
{}
methods
:=
[]
string
{}
methods
Data
=
make
(
map
[
string
]
interface
{})
methodsData
:=
make
(
map
[
string
]
interface
{}
)
)
for
method
,
t
:=
range
BeeApp
.
Handlers
.
routers
{
for
method
,
t
:=
range
BeeApp
.
Handlers
.
routers
{
resultList
:=
new
([][]
string
)
resultList
:=
new
([][]
string
)
...
@@ -121,16 +125,16 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
...
@@ -121,16 +125,16 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
data
[
"Title"
]
=
"Routers"
data
[
"Title"
]
=
"Routers"
execTpl
(
rw
,
data
,
routerAndFilterTpl
,
defaultScriptsTpl
)
execTpl
(
rw
,
data
,
routerAndFilterTpl
,
defaultScriptsTpl
)
case
"filter"
:
case
"filter"
:
content
:=
make
(
map
[
string
]
interface
{})
var
(
content
=
map
[
string
]
interface
{}{
var
fields
=
[]
string
{
"Fields"
:
[]
string
{
fmt
.
Sprintf
(
"Router Pattern"
),
"Router Pattern"
,
fmt
.
Sprintf
(
"Filter Function"
),
"Filter Function"
,
},
}
}
content
[
"Fields"
]
=
fields
filterTypes
=
[]
string
{}
filterTypeData
=
make
(
map
[
string
]
interface
{})
filterTypes
:=
[]
string
{}
)
filterTypeData
:=
make
(
map
[
string
]
interface
{})
if
BeeApp
.
Handlers
.
enableFilter
{
if
BeeApp
.
Handlers
.
enableFilter
{
var
filterType
string
var
filterType
string
...
@@ -165,8 +169,6 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
...
@@ -165,8 +169,6 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
default
:
default
:
rw
.
Write
([]
byte
(
"command not support"
))
rw
.
Write
([]
byte
(
"command not support"
))
}
}
}
else
{
}
}
}
func
printTree
(
resultList
*
[][]
string
,
t
*
Tree
)
{
func
printTree
(
resultList
*
[][]
string
,
t
*
Tree
)
{
...
@@ -180,23 +182,23 @@ func printTree(resultList *[][]string, t *Tree) {
...
@@ -180,23 +182,23 @@ func printTree(resultList *[][]string, t *Tree) {
if
v
,
ok
:=
l
.
runObject
.
(
*
controllerInfo
);
ok
{
if
v
,
ok
:=
l
.
runObject
.
(
*
controllerInfo
);
ok
{
if
v
.
routerType
==
routerTypeBeego
{
if
v
.
routerType
==
routerTypeBeego
{
var
result
=
[]
string
{
var
result
=
[]
string
{
fmt
.
Sprintf
(
"%s"
,
v
.
pattern
)
,
v
.
pattern
,
fmt
.
Sprintf
(
"%s"
,
v
.
methods
),
fmt
.
Sprintf
(
"%s"
,
v
.
methods
),
fmt
.
Sprintf
(
"%s"
,
v
.
controllerType
),
fmt
.
Sprintf
(
"%s"
,
v
.
controllerType
),
}
}
*
resultList
=
append
(
*
resultList
,
result
)
*
resultList
=
append
(
*
resultList
,
result
)
}
else
if
v
.
routerType
==
routerTypeRESTFul
{
}
else
if
v
.
routerType
==
routerTypeRESTFul
{
var
result
=
[]
string
{
var
result
=
[]
string
{
fmt
.
Sprintf
(
"%s"
,
v
.
pattern
)
,
v
.
pattern
,
fmt
.
Sprintf
(
"%s"
,
v
.
methods
),
fmt
.
Sprintf
(
"%s"
,
v
.
methods
),
fmt
.
Sprintf
(
""
)
,
""
,
}
}
*
resultList
=
append
(
*
resultList
,
result
)
*
resultList
=
append
(
*
resultList
,
result
)
}
else
if
v
.
routerType
==
routerTypeHandler
{
}
else
if
v
.
routerType
==
routerTypeHandler
{
var
result
=
[]
string
{
var
result
=
[]
string
{
fmt
.
Sprintf
(
"%s"
,
v
.
pattern
)
,
v
.
pattern
,
fmt
.
Sprintf
(
""
)
,
""
,
fmt
.
Sprintf
(
""
)
,
""
,
}
}
*
resultList
=
append
(
*
resultList
,
result
)
*
resultList
=
append
(
*
resultList
,
result
)
}
}
...
@@ -209,11 +211,15 @@ func printTree(resultList *[][]string, t *Tree) {
...
@@ -209,11 +211,15 @@ func printTree(resultList *[][]string, t *Tree) {
func
profIndex
(
rw
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
func
profIndex
(
rw
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
r
.
ParseForm
()
r
.
ParseForm
()
command
:=
r
.
Form
.
Get
(
"command"
)
command
:=
r
.
Form
.
Get
(
"command"
)
format
:=
r
.
Form
.
Get
(
"format"
)
if
command
==
""
{
data
:=
make
(
map
[
interface
{}]
interface
{})
return
}
var
result
bytes
.
Buffer
var
(
if
command
!=
""
{
format
=
r
.
Form
.
Get
(
"format"
)
data
=
make
(
map
[
interface
{}]
interface
{})
result
bytes
.
Buffer
)
toolbox
.
ProcessInput
(
command
,
&
result
)
toolbox
.
ProcessInput
(
command
,
&
result
)
data
[
"Content"
]
=
result
.
String
()
data
[
"Content"
]
=
result
.
String
()
...
@@ -235,23 +241,19 @@ func profIndex(rw http.ResponseWriter, r *http.Request) {
...
@@ -235,23 +241,19 @@ func profIndex(rw http.ResponseWriter, r *http.Request) {
defaultTpl
=
gcAjaxTpl
defaultTpl
=
gcAjaxTpl
}
}
execTpl
(
rw
,
data
,
profillingTpl
,
defaultTpl
)
execTpl
(
rw
,
data
,
profillingTpl
,
defaultTpl
)
}
}
}
// Healthcheck is a http.Handler calling health checking and showing the result.
// Healthcheck is a http.Handler calling health checking and showing the result.
// it's in "/healthcheck" pattern in admin module.
// it's in "/healthcheck" pattern in admin module.
func
healthcheck
(
rw
http
.
ResponseWriter
,
req
*
http
.
Request
)
{
func
healthcheck
(
rw
http
.
ResponseWriter
,
req
*
http
.
Request
)
{
data
:=
make
(
map
[
interface
{}]
interface
{})
var
(
data
=
make
(
map
[
interface
{}]
interface
{})
var
result
=
[]
string
{}
result
=
[]
string
{}
fields
:=
[]
string
{
resultList
=
new
([][]
string
)
fmt
.
Sprintf
(
"Name"
),
content
=
map
[
string
]
interface
{}{
fmt
.
Sprintf
(
"Message"
),
"Fields"
:
[]
string
{
"Name"
,
"Message"
,
"Status"
},
fmt
.
Sprintf
(
"Status"
),
}
}
resultList
:=
new
([][]
string
)
)
content
:=
make
(
map
[
string
]
interface
{})
for
name
,
h
:=
range
toolbox
.
AdminCheckList
{
for
name
,
h
:=
range
toolbox
.
AdminCheckList
{
if
err
:=
h
.
Check
();
err
!=
nil
{
if
err
:=
h
.
Check
();
err
!=
nil
{
...
@@ -271,8 +273,6 @@ func healthcheck(rw http.ResponseWriter, req *http.Request) {
...
@@ -271,8 +273,6 @@ func healthcheck(rw http.ResponseWriter, req *http.Request) {
}
}
*
resultList
=
append
(
*
resultList
,
result
)
*
resultList
=
append
(
*
resultList
,
result
)
}
}
content
[
"Fields"
]
=
fields
content
[
"Data"
]
=
resultList
content
[
"Data"
]
=
resultList
data
[
"Content"
]
=
content
data
[
"Content"
]
=
content
data
[
"Title"
]
=
"Health Check"
data
[
"Title"
]
=
"Health Check"
...
@@ -288,10 +288,8 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
...
@@ -288,10 +288,8 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
req
.
ParseForm
()
req
.
ParseForm
()
taskname
:=
req
.
Form
.
Get
(
"taskname"
)
taskname
:=
req
.
Form
.
Get
(
"taskname"
)
if
taskname
!=
""
{
if
taskname
!=
""
{
if
t
,
ok
:=
toolbox
.
AdminTaskList
[
taskname
];
ok
{
if
t
,
ok
:=
toolbox
.
AdminTaskList
[
taskname
];
ok
{
err
:=
t
.
Run
()
if
err
:=
t
.
Run
();
err
!=
nil
{
if
err
!=
nil
{
data
[
"Message"
]
=
[]
string
{
"error"
,
fmt
.
Sprintf
(
"%s"
,
err
)}
data
[
"Message"
]
=
[]
string
{
"error"
,
fmt
.
Sprintf
(
"%s"
,
err
)}
}
}
data
[
"Message"
]
=
[]
string
{
"success"
,
fmt
.
Sprintf
(
"%s run success,Now the Status is <br>%s"
,
taskname
,
t
.
GetStatus
())}
data
[
"Message"
]
=
[]
string
{
"success"
,
fmt
.
Sprintf
(
"%s run success,Now the Status is <br>%s"
,
taskname
,
t
.
GetStatus
())}
...
@@ -305,18 +303,18 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
...
@@ -305,18 +303,18 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) {
resultList
:=
new
([][]
string
)
resultList
:=
new
([][]
string
)
var
result
=
[]
string
{}
var
result
=
[]
string
{}
var
fields
=
[]
string
{
var
fields
=
[]
string
{
fmt
.
Sprintf
(
"Task Name"
)
,
"Task Name"
,
fmt
.
Sprintf
(
"Task Spec"
)
,
"Task Spec"
,
fmt
.
Sprintf
(
"Task Status"
)
,
"Task Status"
,
fmt
.
Sprintf
(
"Last Time"
)
,
"Last Time"
,
fmt
.
Sprintf
(
""
)
,
""
,
}
}
for
tname
,
tk
:=
range
toolbox
.
AdminTaskList
{
for
tname
,
tk
:=
range
toolbox
.
AdminTaskList
{
result
=
[]
string
{
result
=
[]
string
{
fmt
.
Sprintf
(
"%s"
,
tname
)
,
tname
,
fmt
.
Sprintf
(
"%s"
,
tk
.
GetSpec
()),
fmt
.
Sprintf
(
"%s"
,
tk
.
GetSpec
()),
fmt
.
Sprintf
(
"%s"
,
tk
.
GetStatus
()),
fmt
.
Sprintf
(
"%s"
,
tk
.
GetStatus
()),
fmt
.
Sprintf
(
"%s"
,
tk
.
GetPrev
()
.
String
()
),
tk
.
GetPrev
()
.
String
(
),
}
}
*
resultList
=
append
(
*
resultList
,
result
)
*
resultList
=
append
(
*
resultList
,
result
)
}
}
...
...
app.go
View file @
46aa340b
...
@@ -61,18 +61,19 @@ func (app *App) Run() {
...
@@ -61,18 +61,19 @@ func (app *App) Run() {
var
(
var
(
err
error
err
error
l
net
.
Listener
l
net
.
Listener
endRunning
=
make
(
chan
bool
,
1
)
)
)
endRunning
:=
make
(
chan
bool
,
1
)
// run cgi server
if
BConfig
.
Listen
.
EnableFcgi
{
if
BConfig
.
Listen
.
EnableFcgi
{
if
BConfig
.
Listen
.
EnableStdIo
{
if
BConfig
.
Listen
.
EnableStdIo
{
err
=
fcgi
.
Serve
(
nil
,
app
.
Handlers
)
// standard I/O
if
err
=
fcgi
.
Serve
(
nil
,
app
.
Handlers
);
err
==
nil
{
// standard I/O
if
err
==
nil
{
BeeLogger
.
Info
(
"Use FCGI via standard I/O"
)
BeeLogger
.
Info
(
"Use FCGI via standard I/O"
)
}
else
{
}
else
{
BeeLogger
.
Info
(
"Cannot use FCGI via standard I/O"
,
err
)
BeeLogger
.
Critical
(
"Cannot use FCGI via standard I/O"
,
err
)
}
return
}
}
}
else
{
if
BConfig
.
Listen
.
HTTPPort
==
0
{
if
BConfig
.
Listen
.
HTTPPort
==
0
{
// remove the Socket file before start
// remove the Socket file before start
if
utils
.
FileExists
(
addr
)
{
if
utils
.
FileExists
(
addr
)
{
...
@@ -85,15 +86,20 @@ func (app *App) Run() {
...
@@ -85,15 +86,20 @@ func (app *App) Run() {
if
err
!=
nil
{
if
err
!=
nil
{
BeeLogger
.
Critical
(
"Listen: "
,
err
)
BeeLogger
.
Critical
(
"Listen: "
,
err
)
}
}
err
=
fcgi
.
Serve
(
l
,
app
.
Handlers
)
if
err
=
fcgi
.
Serve
(
l
,
app
.
Handlers
);
err
!=
nil
{
BeeLogger
.
Critical
(
"fcgi.Serve: "
,
err
)
}
}
}
else
{
return
if
BConfig
.
Listen
.
Graceful
{
}
httpsAddr
:=
BConfig
.
Listen
.
HTTPSAddr
app
.
Server
.
Addr
=
httpsAddr
app
.
Server
.
Handler
=
app
.
Handlers
app
.
Server
.
Handler
=
app
.
Handlers
app
.
Server
.
ReadTimeout
=
time
.
Duration
(
BConfig
.
Listen
.
ServerTimeOut
)
*
time
.
Second
app
.
Server
.
ReadTimeout
=
time
.
Duration
(
BConfig
.
Listen
.
ServerTimeOut
)
*
time
.
Second
app
.
Server
.
WriteTimeout
=
time
.
Duration
(
BConfig
.
Listen
.
ServerTimeOut
)
*
time
.
Second
app
.
Server
.
WriteTimeout
=
time
.
Duration
(
BConfig
.
Listen
.
ServerTimeOut
)
*
time
.
Second
// run graceful mode
if
BConfig
.
Listen
.
Graceful
{
httpsAddr
:=
BConfig
.
Listen
.
HTTPSAddr
app
.
Server
.
Addr
=
httpsAddr
if
BConfig
.
Listen
.
HTTPSEnable
{
if
BConfig
.
Listen
.
HTTPSEnable
{
go
func
()
{
go
func
()
{
time
.
Sleep
(
20
*
time
.
Microsecond
)
time
.
Sleep
(
20
*
time
.
Microsecond
)
...
@@ -104,8 +110,7 @@ func (app *App) Run() {
...
@@ -104,8 +110,7 @@ func (app *App) Run() {
server
:=
grace
.
NewServer
(
httpsAddr
,
app
.
Handlers
)
server
:=
grace
.
NewServer
(
httpsAddr
,
app
.
Handlers
)
server
.
Server
.
ReadTimeout
=
app
.
Server
.
ReadTimeout
server
.
Server
.
ReadTimeout
=
app
.
Server
.
ReadTimeout
server
.
Server
.
WriteTimeout
=
app
.
Server
.
WriteTimeout
server
.
Server
.
WriteTimeout
=
app
.
Server
.
WriteTimeout
err
:=
server
.
ListenAndServeTLS
(
BConfig
.
Listen
.
HTTPSCertFile
,
BConfig
.
Listen
.
HTTPSKeyFile
)
if
err
:=
server
.
ListenAndServeTLS
(
BConfig
.
Listen
.
HTTPSCertFile
,
BConfig
.
Listen
.
HTTPSKeyFile
);
err
!=
nil
{
if
err
!=
nil
{
BeeLogger
.
Critical
(
"ListenAndServeTLS: "
,
err
,
fmt
.
Sprintf
(
"%d"
,
os
.
Getpid
()))
BeeLogger
.
Critical
(
"ListenAndServeTLS: "
,
err
,
fmt
.
Sprintf
(
"%d"
,
os
.
Getpid
()))
time
.
Sleep
(
100
*
time
.
Microsecond
)
time
.
Sleep
(
100
*
time
.
Microsecond
)
endRunning
<-
true
endRunning
<-
true
...
@@ -120,20 +125,19 @@ func (app *App) Run() {
...
@@ -120,20 +125,19 @@ func (app *App) Run() {
if
BConfig
.
Listen
.
ListenTCP4
{
if
BConfig
.
Listen
.
ListenTCP4
{
server
.
Network
=
"tcp4"
server
.
Network
=
"tcp4"
}
}
err
:=
server
.
ListenAndServe
()
if
err
:=
server
.
ListenAndServe
();
err
!=
nil
{
if
err
!=
nil
{
BeeLogger
.
Critical
(
"ListenAndServe: "
,
err
,
fmt
.
Sprintf
(
"%d"
,
os
.
Getpid
()))
BeeLogger
.
Critical
(
"ListenAndServe: "
,
err
,
fmt
.
Sprintf
(
"%d"
,
os
.
Getpid
()))
time
.
Sleep
(
100
*
time
.
Microsecond
)
time
.
Sleep
(
100
*
time
.
Microsecond
)
endRunning
<-
true
endRunning
<-
true
}
}
}()
}()
}
}
}
else
{
<-
endRunning
app
.
Server
.
Addr
=
addr
return
app
.
Server
.
Handler
=
app
.
Handlers
}
app
.
Server
.
ReadTimeout
=
time
.
Duration
(
BConfig
.
Listen
.
ServerTimeOut
)
*
time
.
Second
app
.
Server
.
WriteTimeout
=
time
.
Duration
(
BConfig
.
Listen
.
ServerTimeOut
)
*
time
.
Second
// run normal mode
app
.
Server
.
Addr
=
addr
if
BConfig
.
Listen
.
HTTPSEnable
{
if
BConfig
.
Listen
.
HTTPSEnable
{
go
func
()
{
go
func
()
{
time
.
Sleep
(
20
*
time
.
Microsecond
)
time
.
Sleep
(
20
*
time
.
Microsecond
)
...
@@ -141,15 +145,13 @@ func (app *App) Run() {
...
@@ -141,15 +145,13 @@ func (app *App) Run() {
app
.
Server
.
Addr
=
fmt
.
Sprintf
(
"%s:%d"
,
BConfig
.
Listen
.
HTTPSAddr
,
BConfig
.
Listen
.
HTTPSPort
)
app
.
Server
.
Addr
=
fmt
.
Sprintf
(
"%s:%d"
,
BConfig
.
Listen
.
HTTPSAddr
,
BConfig
.
Listen
.
HTTPSPort
)
}
}
BeeLogger
.
Info
(
"https server Running on %s"
,
app
.
Server
.
Addr
)
BeeLogger
.
Info
(
"https server Running on %s"
,
app
.
Server
.
Addr
)
err
:=
app
.
Server
.
ListenAndServeTLS
(
BConfig
.
Listen
.
HTTPSCertFile
,
BConfig
.
Listen
.
HTTPSKeyFile
)
if
err
:=
app
.
Server
.
ListenAndServeTLS
(
BConfig
.
Listen
.
HTTPSCertFile
,
BConfig
.
Listen
.
HTTPSKeyFile
);
err
!=
nil
{
if
err
!=
nil
{
BeeLogger
.
Critical
(
"ListenAndServeTLS: "
,
err
)
BeeLogger
.
Critical
(
"ListenAndServeTLS: "
,
err
)
time
.
Sleep
(
100
*
time
.
Microsecond
)
time
.
Sleep
(
100
*
time
.
Microsecond
)
endRunning
<-
true
endRunning
<-
true
}
}
}()
}()
}
}
if
BConfig
.
Listen
.
HTTPEnable
{
if
BConfig
.
Listen
.
HTTPEnable
{
go
func
()
{
go
func
()
{
app
.
Server
.
Addr
=
addr
app
.
Server
.
Addr
=
addr
...
@@ -162,16 +164,14 @@ func (app *App) Run() {
...
@@ -162,16 +164,14 @@ func (app *App) Run() {
endRunning
<-
true
endRunning
<-
true
return
return
}
}
err
=
app
.
Server
.
Serve
(
ln
)
if
err
=
app
.
Server
.
Serve
(
ln
);
err
!=
nil
{
if
err
!=
nil
{
BeeLogger
.
Critical
(
"ListenAndServe: "
,
err
)
BeeLogger
.
Critical
(
"ListenAndServe: "
,
err
)
time
.
Sleep
(
100
*
time
.
Microsecond
)
time
.
Sleep
(
100
*
time
.
Microsecond
)
endRunning
<-
true
endRunning
<-
true
return
return
}
}
}
else
{
}
else
{
err
:=
app
.
Server
.
ListenAndServe
()
if
err
:=
app
.
Server
.
ListenAndServe
();
err
!=
nil
{
if
err
!=
nil
{
BeeLogger
.
Critical
(
"ListenAndServe: "
,
err
)
BeeLogger
.
Critical
(
"ListenAndServe: "
,
err
)
time
.
Sleep
(
100
*
time
.
Microsecond
)
time
.
Sleep
(
100
*
time
.
Microsecond
)
endRunning
<-
true
endRunning
<-
true
...
@@ -179,9 +179,6 @@ func (app *App) Run() {
...
@@ -179,9 +179,6 @@ func (app *App) Run() {
}
}
}()
}()
}
}
}
}
<-
endRunning
<-
endRunning
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment