Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
D
dlink
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
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
zhaowei
dlink
Commits
81615603
Commit
81615603
authored
Jan 19, 2022
by
wenmo
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
修复前端多处bug
parent
053967f7
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
110 additions
and
84 deletions
+110
-84
Job2MysqlHandler.java
...k-admin/src/main/java/com/dlink/job/Job2MysqlHandler.java
+2
-1
GatewayType.java
...-gateway/src/main/java/com/dlink/gateway/GatewayType.java
+22
-0
defaultSettings.ts
dlink-web/config/defaultSettings.ts
+1
-1
routes.ts
dlink-web/config/routes.ts
+3
-3
index.tsx
dlink-web/src/components/Chart/index.tsx
+19
-10
index.tsx
...src/components/Studio/StudioConsole/StudioTable/index.tsx
+1
-1
index.tsx
...omponents/Studio/StudioLeftTool/StudioConnector/index.tsx
+3
-3
menu.ts
dlink-web/src/locales/zh-CN/menu.ts
+1
-1
model.ts
dlink-web/src/pages/FlinkSqlStudio/model.ts
+43
-60
Welcome.tsx
dlink-web/src/pages/Welcome.tsx
+15
-4
No files found.
dlink-admin/src/main/java/com/dlink/job/Job2MysqlHandler.java
View file @
81615603
...
@@ -3,6 +3,7 @@ package com.dlink.job;
...
@@ -3,6 +3,7 @@ package com.dlink.job;
import
cn.hutool.json.JSONUtil
;
import
cn.hutool.json.JSONUtil
;
import
com.dlink.assertion.Asserts
;
import
com.dlink.assertion.Asserts
;
import
com.dlink.context.SpringContextUtils
;
import
com.dlink.context.SpringContextUtils
;
import
com.dlink.gateway.GatewayType
;
import
com.dlink.model.Cluster
;
import
com.dlink.model.Cluster
;
import
com.dlink.model.History
;
import
com.dlink.model.History
;
import
com.dlink.service.ClusterService
;
import
com.dlink.service.ClusterService
;
...
@@ -66,7 +67,7 @@ public class Job2MysqlHandler implements JobHandler {
...
@@ -66,7 +67,7 @@ public class Job2MysqlHandler implements JobHandler {
Job
job
=
JobContextHolder
.
getJob
();
Job
job
=
JobContextHolder
.
getJob
();
History
history
=
new
History
();
History
history
=
new
History
();
history
.
setId
(
job
.
getId
());
history
.
setId
(
job
.
getId
());
if
(
Asserts
.
isNullString
(
job
.
getJobId
())){
if
(
job
.
isUseGateway
()&&
Asserts
.
isNullString
(
job
.
getJobId
())){
job
.
setJobId
(
"unknown-"
+
LocalDateTime
.
now
().
toString
());
job
.
setJobId
(
"unknown-"
+
LocalDateTime
.
now
().
toString
());
}
}
history
.
setJobId
(
job
.
getJobId
());
history
.
setJobId
(
job
.
getJobId
());
...
...
dlink-gateway/src/main/java/com/dlink/gateway/GatewayType.java
View file @
81615603
...
@@ -46,4 +46,26 @@ public enum GatewayType {
...
@@ -46,4 +46,26 @@ public enum GatewayType {
}
}
return
false
;
return
false
;
}
}
public
static
boolean
isDeployCluster
(
String
type
){
switch
(
get
(
type
)){
case
YARN_APPLICATION:
case
YARN_PER_JOB:
case
KUBERNETES_APPLICATION:
return
true
;
default
:
return
false
;
}
}
public
boolean
isDeployCluster
(){
switch
(
value
){
case
"ya"
:
case
"ypj"
:
case
"ka"
:
return
true
;
default
:
return
false
;
}
}
}
}
dlink-web/config/defaultSettings.ts
View file @
81615603
...
@@ -12,7 +12,7 @@ const Settings: LayoutSettings & {
...
@@ -12,7 +12,7 @@ const Settings: LayoutSettings & {
fixedHeader
:
false
,
fixedHeader
:
false
,
fixSiderbar
:
true
,
fixSiderbar
:
true
,
colorWeak
:
false
,
colorWeak
:
false
,
title
:
'Dinky'
,
title
:
'Dinky
实时计算平台
'
,
pwa
:
false
,
pwa
:
false
,
logo
:
'dinky.svg'
,
logo
:
'dinky.svg'
,
iconfontUrl
:
''
,
iconfontUrl
:
''
,
...
...
dlink-web/config/routes.ts
View file @
81615603
...
@@ -84,9 +84,9 @@ export default [
...
@@ -84,9 +84,9 @@ export default [
component
:
'./Settings'
,
component
:
'./Settings'
,
},
},
{
{
path
:
'/
welcome
'
,
path
:
'/
about
'
,
name
:
'
home
'
,
name
:
'
about
'
,
icon
:
'
hom
e'
,
icon
:
'
smil
e'
,
component
:
'./Welcome'
,
component
:
'./Welcome'
,
},
},
{
{
...
...
dlink-web/src/components/Chart/index.tsx
View file @
81615603
...
@@ -18,7 +18,7 @@ const {Option} = Select;
...
@@ -18,7 +18,7 @@ const {Option} = Select;
const
Chart
=
(
props
:
any
)
=>
{
const
Chart
=
(
props
:
any
)
=>
{
const
{
current
,
result
,
height
,
dispatch
}
=
props
;
const
{
current
,
result
,
height
,
dispatch
}
=
props
;
const
[
config
,
setConfig
]
=
useState
(
{}
);
const
[
config
,
setConfig
]
=
useState
(
undefined
);
const
[
type
,
setType
]
=
useState
<
string
>
(
CHART
.
LINE
);
const
[
type
,
setType
]
=
useState
<
string
>
(
CHART
.
LINE
);
const
[
form
]
=
Form
.
useForm
();
const
[
form
]
=
Form
.
useForm
();
...
@@ -34,6 +34,7 @@ const Chart = (props:any) => {
...
@@ -34,6 +34,7 @@ const Chart = (props:any) => {
const
onValuesChange
=
(
change
:
any
,
all
:
any
)
=>
{
const
onValuesChange
=
(
change
:
any
,
all
:
any
)
=>
{
if
(
change
.
type
){
if
(
change
.
type
){
setConfig
(
undefined
);
setType
(
change
.
type
);
setType
(
change
.
type
);
props
.
saveChart
({
type
:
change
.
type
});
props
.
saveChart
({
type
:
change
.
type
});
}
}
...
@@ -73,15 +74,23 @@ const Chart = (props:any) => {
...
@@ -73,15 +74,23 @@ const Chart = (props:any) => {
}
}
switch
(
current
.
console
.
chart
.
type
){
switch
(
current
.
console
.
chart
.
type
){
case
CHART
.
LINE
:
case
CHART
.
LINE
:
return
<
Line
data=
{
current
.
console
.
result
.
result
.
rowData
}
{
...
config
}
/>;
if
(
config
){
case
CHART
.
BAR
:
return
<
Line
data=
{
current
.
console
.
result
.
result
.
rowData
}
{
...
config
}
/>;
return
<
Bar
data=
{
current
.
console
.
result
.
result
.
rowData
}
{
...
config
}
/>;
}
else
{
case
CHART
.
PIE
:
return
<
Empty
image=
{
Empty
.
PRESENTED_IMAGE_SIMPLE
}
/>;
if
(
config
.
angleField
){
}
return
<
Pie
data=
{
current
.
console
.
result
.
result
.
rowData
}
{
...
config
}
/>;
case
CHART
.
BAR
:
}
else
{
if
(
config
){
return
<
Empty
image=
{
Empty
.
PRESENTED_IMAGE_SIMPLE
}
/>;
return
<
Bar
data=
{
current
.
console
.
result
.
result
.
rowData
}
{
...
config
}
/>;
}
}
else
{
return
<
Empty
image=
{
Empty
.
PRESENTED_IMAGE_SIMPLE
}
/>;
}
case
CHART
.
PIE
:
if
(
config
&&
config
.
angleField
){
return
<
Pie
data=
{
current
.
console
.
result
.
result
.
rowData
}
{
...
config
}
/>;
}
else
{
return
<
Empty
image=
{
Empty
.
PRESENTED_IMAGE_SIMPLE
}
/>;
}
default
:
default
:
return
<
Line
data=
{
current
.
console
.
result
.
result
.
rowData
}
{
...
config
}
/>;
return
<
Line
data=
{
current
.
console
.
result
.
result
.
rowData
}
{
...
config
}
/>;
}
}
...
...
dlink-web/src/components/Studio/StudioConsole/StudioTable/index.tsx
View file @
81615603
...
@@ -12,7 +12,7 @@ const StudioTable = (props: any) => {
...
@@ -12,7 +12,7 @@ const StudioTable = (props: any) => {
const
getColumns
=
(
columns
:
[])
=>
{
const
getColumns
=
(
columns
:
[])
=>
{
let
datas
:
any
=
[];
let
datas
:
any
=
[];
columns
.
map
((
item
)
=>
{
columns
?
.
map
((
item
)
=>
{
datas
.
push
({
datas
.
push
({
field
:
item
,
field
:
item
,
});
});
...
...
dlink-web/src/components/Studio/StudioLeftTool/StudioConnector/index.tsx
View file @
81615603
...
@@ -153,10 +153,10 @@ const StudioConnector = (props: any) => {
...
@@ -153,10 +153,10 @@ const StudioConnector = (props: any) => {
const
getColumns
=
()
=>
{
const
getColumns
=
()
=>
{
let
columns
:
any
=
[{
let
columns
:
any
=
[{
title
:
"表名"
,
title
:
"表名"
,
dataIndex
:
"tablename"
,
dataIndex
:
"table
name"
,
key
:
"tablename"
,
key
:
"table
name"
,
sorter
:
true
,
sorter
:
true
,
...
getColumnSearchProps
(
"tablename"
),
...
getColumnSearchProps
(
"table
name"
),
},
{
},
{
title
:
'操作'
,
title
:
'操作'
,
dataIndex
:
'option'
,
dataIndex
:
'option'
,
...
...
dlink-web/src/locales/zh-CN/menu.ts
View file @
81615603
export
default
{
export
default
{
'menu.welcome'
:
'欢迎'
,
'menu.welcome'
:
'欢迎'
,
'menu.more-blocks'
:
'更多区块'
,
'menu.more-blocks'
:
'更多区块'
,
'menu.
home'
:
'更新历史
'
,
'menu.
about'
:
'关于
'
,
'menu.admin'
:
'管理页'
,
'menu.admin'
:
'管理页'
,
'menu.admin.sub-page'
:
'二级管理页'
,
'menu.admin.sub-page'
:
'二级管理页'
,
'menu.login'
:
'登录'
,
'menu.login'
:
'登录'
,
...
...
dlink-web/src/pages/FlinkSqlStudio/model.ts
View file @
81615603
...
@@ -261,12 +261,8 @@ const Model: ModelType = {
...
@@ -261,12 +261,8 @@ const Model: ModelType = {
}
}
return
{
return
{
...
state
,
...
state
,
current
:
{
current
:
newCurrent
,
...
newCurrent
tabs
,
},
tabs
:
{
...
tabs
},
};
};
},
},
saveCurrentPath
(
state
,
{
payload
})
{
saveCurrentPath
(
state
,
{
payload
})
{
...
@@ -299,24 +295,22 @@ const Model: ModelType = {
...
@@ -299,24 +295,22 @@ const Model: ModelType = {
}
}
return
{
return
{
...
state
,
...
state
,
current
:
{...
newCurrent
}
,
current
:
newCurrent
,
tabs
:
{...
newTabs
}
,
tabs
:
newTabs
,
};
};
},
},
saveTabs
(
state
,
{
payload
})
{
saveTabs
(
state
,
{
payload
})
{
let
newCurrent
=
state
.
current
;
let
newCurrent
=
state
.
current
;
for
(
let
i
=
0
;
i
<
payload
.
panes
.
length
;
i
++
)
{
for
(
let
i
=
0
;
i
<
payload
.
panes
.
length
;
i
++
)
{
if
(
payload
.
panes
[
i
].
key
==
payload
.
activeKey
)
{
if
(
payload
.
panes
[
i
].
key
==
payload
.
activeKey
)
{
newCurrent
=
{...
payload
.
panes
[
i
]}
;
newCurrent
=
payload
.
panes
[
i
]
;
}
}
}
}
if
(
payload
.
panes
.
length
==
=
0
){
if
(
payload
.
panes
.
length
==
0
){
return
{
return
{
...
state
,
...
state
,
current
:
undefined
,
current
:
undefined
,
tabs
:
{
tabs
:
payload
,
...
payload
,
},
currentPath
:
[
'引导页'
],
currentPath
:
[
'引导页'
],
};
};
}
}
...
@@ -326,9 +320,7 @@ const Model: ModelType = {
...
@@ -326,9 +320,7 @@ const Model: ModelType = {
...
newCurrent
,
...
newCurrent
,
isModified
:
false
,
isModified
:
false
,
},
},
tabs
:
{
tabs
:
payload
,
...
payload
,
},
currentPath
:
newCurrent
.
path
,
currentPath
:
newCurrent
.
path
,
};
};
},
},
...
@@ -340,28 +332,27 @@ const Model: ModelType = {
...
@@ -340,28 +332,27 @@ const Model: ModelType = {
break
;
break
;
}
}
}
}
const
newCurrent
=
newTabs
.
panes
[
newTabs
.
panes
.
length
-
1
];
const
newCurrent
=
undefined
;
if
(
newTabs
.
panes
.
length
>
0
){
newCurrent
=
newTabs
.
panes
[
newTabs
.
panes
.
length
-
1
];
}
if
(
newTabs
.
activeKey
==
payload
)
{
if
(
newTabs
.
activeKey
==
payload
)
{
newTabs
.
activeKey
=
newCurrent
.
key
;
newTabs
.
activeKey
=
newCurrent
.
key
;
}
}
return
{
return
{
...
state
,
...
state
,
current
:
{
current
:
newCurrent
,
...
newCurrent
,
tabs
:
newTabs
,
},
tabs
:
{
...
newTabs
,
},
};
};
},
},
closeTabs
(
state
,
{
payload
})
{
closeTabs
(
state
,
{
payload
})
{
const
{
deleteType
,
current
}
=
payload
;
const
{
deleteType
,
current
}
=
payload
;
const
newTabs
=
state
.
tabs
;
const
newTabs
=
state
.
tabs
;
const
firstKey
=
newTabs
.
panes
[
0
].
key
;
const
firstKey
=
newTabs
.
panes
[
0
].
key
;
le
t
newCurrent
=
newTabs
.
panes
[
0
];
cons
t
newCurrent
=
newTabs
.
panes
[
0
];
if
(
deleteType
==
=
'CLOSE_OTHER'
)
{
if
(
deleteType
==
'CLOSE_OTHER'
)
{
const
keys
=
[
firstKey
,
current
.
key
];
const
keys
=
[
firstKey
,
current
.
key
];
newCurrent
=
{...
current
}
;
newCurrent
=
current
;
newTabs
.
activeKey
=
current
.
key
;
newTabs
.
activeKey
=
current
.
key
;
newTabs
.
panes
=
newTabs
.
panes
.
filter
(
item
=>
keys
.
includes
(
item
.
key
));
newTabs
.
panes
=
newTabs
.
panes
.
filter
(
item
=>
keys
.
includes
(
item
.
key
));
}
else
{
}
else
{
...
@@ -371,31 +362,23 @@ const Model: ModelType = {
...
@@ -371,31 +362,23 @@ const Model: ModelType = {
return
{
return
{
...
state
,
...
state
,
current
:
{
current
:
newCurrent
,
...
newCurrent
tabs
:
newTabs
},
tabs
:
{
...
newTabs
,
}
};
};
},
},
changeActiveKey
(
state
,
{
payload
})
{
changeActiveKey
(
state
,
{
payload
})
{
const
{
tabs
}
=
state
;
const
newTabs
=
state
.
tabs
;
tabs
.
activeKey
=
payload
;
let
newCurrent
=
state
.
current
;
let
newCurrent
=
state
.
current
;
for
(
let
i
=
0
;
i
<
tabs
.
panes
.
length
;
i
++
)
{
newTabs
.
activeKey
=
payload
;
if
(
tabs
.
panes
[
i
].
key
==
tabs
.
activeKey
)
{
for
(
let
i
=
0
;
i
<
newTabs
.
panes
.
length
;
i
++
)
{
newCurrent
=
{...
tabs
.
panes
[
i
]};
if
(
newTabs
.
panes
[
i
].
key
==
payload
)
{
newCurrent
=
newTabs
.
panes
[
i
];
}
}
}
}
return
{
return
{
...
state
,
...
state
,
current
:
{
current
:
newCurrent
,
...
newCurrent
,
tabs
:
newTabs
,
},
tabs
:
{
...
tabs
,
},
currentPath
:
newCurrent
.
path
,
currentPath
:
newCurrent
.
path
,
};
};
},
},
...
@@ -403,24 +386,24 @@ const Model: ModelType = {
...
@@ -403,24 +386,24 @@ const Model: ModelType = {
const
newTabs
=
state
.
tabs
;
const
newTabs
=
state
.
tabs
;
const
newCurrent
=
state
.
current
;
const
newCurrent
=
state
.
current
;
for
(
let
i
=
0
;
i
<
newTabs
.
panes
.
length
;
i
++
)
{
for
(
let
i
=
0
;
i
<
newTabs
.
panes
.
length
;
i
++
)
{
if
(
newTabs
.
panes
[
i
].
key
==
=
payload
.
key
)
{
if
(
newTabs
.
panes
[
i
].
key
==
payload
.
key
)
{
newTabs
.
panes
[
i
].
task
=
{...
payload
}
;
newTabs
.
panes
[
i
].
task
=
payload
;
newTabs
.
panes
[
i
].
isModified
=
false
;
newTabs
.
panes
[
i
].
isModified
=
false
;
if
(
newCurrent
.
key
==
=
payload
.
key
){
if
(
newCurrent
.
key
==
payload
.
key
){
newCurrent
=
{...
newTabs
.
panes
[
i
]}
;
newCurrent
=
newTabs
.
panes
[
i
]
;
}
}
}
}
}
}
return
{
return
{
...
state
,
...
state
,
current
:
{...
newCurrent
}
,
current
:
newCurrent
,
tabs
:
{...
newTabs
}
,
tabs
:
newTabs
,
};
};
},
},
saveSession
(
state
,
{
payload
})
{
saveSession
(
state
,
{
payload
})
{
return
{
return
{
...
state
,
...
state
,
session
:
[...
payload
]
,
session
:
payload
,
};
};
},
},
showRightClickMenu
(
state
,
{
payload
})
{
showRightClickMenu
(
state
,
{
payload
})
{
...
@@ -450,16 +433,16 @@ const Model: ModelType = {
...
@@ -450,16 +433,16 @@ const Model: ModelType = {
const
newTabs
=
state
?.
tabs
;
const
newTabs
=
state
?.
tabs
;
let
newCurrent
=
state
?.
current
;
let
newCurrent
=
state
?.
current
;
for
(
let
i
=
0
;
i
<
newTabs
.
panes
.
length
;
i
++
)
{
for
(
let
i
=
0
;
i
<
newTabs
.
panes
.
length
;
i
++
)
{
if
(
newTabs
.
panes
[
i
].
key
==
=
newTabs
.
activeKey
)
{
if
(
newTabs
.
panes
[
i
].
key
==
newTabs
.
activeKey
)
{
newTabs
.
panes
[
i
].
console
.
result
.
result
=
{...
payload
}
;
newTabs
.
panes
[
i
].
console
.
result
.
result
=
payload
;
newCurrent
=
{...
newTabs
.
panes
[
i
]}
;
newCurrent
=
newTabs
.
panes
[
i
]
;
break
;
break
;
}
}
}
}
return
{
return
{
...
state
,
...
state
,
current
:
{...
newCurrent
}
,
current
:
newCurrent
,
tabs
:
{...
newTabs
}
,
tabs
:
newTabs
,
};
};
},
},
saveCluster
(
state
,
{
payload
})
{
saveCluster
(
state
,
{
payload
})
{
...
@@ -491,16 +474,16 @@ const Model: ModelType = {
...
@@ -491,16 +474,16 @@ const Model: ModelType = {
let
newTabs
=
state
?.
tabs
;
let
newTabs
=
state
?.
tabs
;
let
newCurrent
=
state
?.
current
;
let
newCurrent
=
state
?.
current
;
for
(
let
i
=
0
;
i
<
newTabs
.
panes
.
length
;
i
++
)
{
for
(
let
i
=
0
;
i
<
newTabs
.
panes
.
length
;
i
++
)
{
if
(
newTabs
.
panes
[
i
].
key
==
=
newTabs
.
activeKey
)
{
if
(
newTabs
.
panes
[
i
].
key
==
newTabs
.
activeKey
)
{
newTabs
.
panes
[
i
].
console
.
chart
=
{...
payload
}
;
newTabs
.
panes
[
i
].
console
.
chart
=
payload
;
newCurrent
=
{...
newTabs
.
panes
[
i
]}
;
newCurrent
=
newTabs
.
panes
[
i
]
;
break
;
break
;
}
}
}
}
return
{
return
{
...
state
,
...
state
,
current
:
{...
newCurrent
}
,
current
:
newCurrent
,
tabs
:
{...
newTabs
}
,
tabs
:
newTabs
,
};
};
},
},
},
},
...
...
dlink-web/src/pages/Welcome.tsx
View file @
81615603
import
React
from
'react'
;
import
React
from
'react'
;
import
{
PageContainer
}
from
'@ant-design/pro-layout'
;
import
{
Card
,
Alert
,
Typography
,
Timeline
}
from
'antd'
;
import
{
Card
,
Alert
,
Typography
,
Timeline
}
from
'antd'
;
import
{
useIntl
,
FormattedMessage
}
from
'umi'
;
import
{
useIntl
,
FormattedMessage
}
from
'umi'
;
import
styles
from
'./Welcome.less'
;
import
styles
from
'./Welcome.less'
;
...
@@ -15,12 +14,12 @@ const CodePreview: React.FC = ({ children }) => (
...
@@ -15,12 +14,12 @@ const CodePreview: React.FC = ({ children }) => (
export
default
():
React
.
ReactNode
=>
{
export
default
():
React
.
ReactNode
=>
{
const
intl
=
useIntl
();
const
intl
=
useIntl
();
return
(
return
(
<
PageContainer
>
<>
<
Card
>
<
Card
>
<
Alert
<
Alert
message=
{
intl
.
formatMessage
({
message=
{
intl
.
formatMessage
({
id
:
'pages.welcome.alertMessage'
,
id
:
'pages.welcome.alertMessage'
,
defaultMessage
:
'实时计算平台 D
link
即将发布,目前为体验版,版本号为 0.5.0。'
,
defaultMessage
:
'实时计算平台 D
inky
即将发布,目前为体验版,版本号为 0.5.0。'
,
})
}
})
}
type=
"success"
type=
"success"
showIcon
showIcon
...
@@ -575,11 +574,23 @@ export default (): React.ReactNode => {
...
@@ -575,11 +574,23 @@ export default (): React.ReactNode => {
<
li
>
<
li
>
<
Link
>
修复 作业非remote作业进行remote语法校验的问题
</
Link
>
<
Link
>
修复 作业非remote作业进行remote语法校验的问题
</
Link
>
</
li
>
</
li
>
<
li
>
<
Link
>
增加 dlink-client-hadoop 版本定制依赖
</
Link
>
</
li
>
<
li
>
<
Link
>
优化 菜单
</
Link
>
</
li
>
<
li
>
<
Link
>
优化 pom及升级log4j至最新
</
Link
>
</
li
>
<
li
>
<
Link
>
修复 前端多处bug
</
Link
>
</
li
>
</
ul
>
</
ul
>
</
Paragraph
>
</
Paragraph
>
</
Timeline
.
Item
>
</
Timeline
.
Item
>
</
Timeline
>
</
Timeline
>
</
Card
>
</
Card
>
</
PageContainer
>
</>
);
);
};
};
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