Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
X
xform
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
田翔
xform
Commits
d1bfa69c
Commit
d1bfa69c
authored
Jan 09, 2024
by
罗剑
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: 提交OCR身份认证
parent
d1f6b8d3
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
576 additions
and
11 deletions
+576
-11
process.js
src/apis/process.js
+1
-0
settings.js
src/core/FormDesigner/config/settings.js
+140
-0
index.js
src/core/widgets/advanced/IDCard/index.js
+313
-0
index.less
src/core/widgets/advanced/IDCard/index.less
+57
-0
index.js
src/core/widgets/advanced/index.js
+3
-0
index.js
src/core/widgets/settings/groupStyle/CheckBoxGroup/index.js
+40
-0
index.less
...core/widgets/settings/groupStyle/CheckBoxGroup/index.less
+9
-0
index.js
src/core/widgets/settings/groupStyle/index.js
+13
-11
No files found.
src/apis/process.js
View file @
d1bfa69c
...
...
@@ -11,6 +11,7 @@ const url = (REACT_APP_ENV || 'dev') !== 'dev' ? `${YOURSELFER_SERVER}/${BASEURL
export
const
uploadFileUrl
=
`
${
BASEURL
}
/UploaderFiles`
;
// 上传文件链接
export
const
downloadFileUrl
=
`
${
BASEURL
}
/DownloadFiles`
;
// 下载/播放文件链接
export
const
uploadCardFileUrl
=
`
${
BASEURL
}
/CardOCRUpLoad`
;
// 上传文件链接
/** @End */
...
...
src/core/FormDesigner/config/settings.js
View file @
d1bfa69c
...
...
@@ -2516,6 +2516,146 @@ const advancedWidgets = [
},
},
},
{
text
:
'身份证识别'
,
name
:
'身份证识别'
,
icon
:
<
IconPack
.
FileUpload
/>
,
schema
:
{
title
:
'身份证'
,
type
:
'string'
,
widget
:
'IDCard'
,
width
:
'100%'
,
preview
:
true
,
},
setting
:
{
widget
:
{
title
:
'控件类型'
,
type
:
'string'
,
widget
:
'WidgetType'
,
displayType
:
'row'
,
labelWidth
:
80
,
},
$id
:
{
title
:
'数据源'
,
type
:
'string'
,
widget
:
'FieldNames'
,
required
:
true
,
},
title
:
{
title
:
'展示名称'
,
type
:
'string'
,
required
:
true
,
},
description
:
{
title
:
'字段说明'
,
type
:
'string'
,
},
showField
:{
title
:
'身份信息'
,
type
:
'array'
,
widget
:
'CheckBoxGroup'
,
default
:
[],
},
// hiddenCondition: {
// title: '隐藏条件',
// type: 'string',
// description: '所有形态默认显示',
// widget: 'HiddenCondition'
// },
// options: {
// title: '',
// name: '选项',
// type: 'array',
// default: [{ value: '选项一' }],
// widget: 'SimpleList',
// dependencies: ['sourceType', 'color', 'isMultiple']
// },
// fileType: {
// title: '文件类型',
// type: 'string',
// widget: 'FileTypeSelect',
// enum: ['图片'],
// enumNames: ['图片'],
// default: '全部',
// },
durationTime
:
{
title
:
"{{formData.fileType+'最大时长'}}"
,
type
:
'number'
,
description
:
'秒'
,
widget
:
'slider'
,
default
:
60
,
min
:
1
,
max
:
60
,
hidden
:
"{{!['音频', '视频'].includes(formData.fileType)}}"
,
dependencies
:
[
'fileType'
]
},
disabled
:
{
title
:
'只读'
,
type
:
'boolean'
,
widget
:
'checkbox'
,
default
:
false
,
width
:
'50%'
,
},
required
:
{
title
:
'必填'
,
type
:
'boolean'
,
widget
:
'checkbox'
,
default
:
false
,
width
:
'50%'
,
},
// preview: {
// title: '预览',
// type: 'boolean',
// widget: 'checkbox',
// default: true,
// width: '33%',
// },
// download: {
// title: '下载',
// type: 'boolean',
// widget: 'checkbox',
// default: true,
// width: '33%',
// },
photo
:
{
title
:
'允许从相册选取'
,
type
:
'boolean'
,
widget
:
'BooleanSwitch'
,
description
:
'仅支持移动端'
,
default
:
false
,
hidden
:
"{{!['图片', '视频'].includes(formData.fileType)}}"
},
watermark
:
{
title
:
'水印'
,
type
:
'boolean'
,
widget
:
'BooleanSwitch'
,
description
:
'图片是否添加水印(仅支持移动端)'
,
default
:
false
,
hidden
:
"{{!['图片'].includes(formData.fileType)}}"
},
groupStyle
:
{
title
:
'控件样式'
,
type
:
'object'
,
properties
:
{}
},
width
:
{
title
:
'元素宽度'
,
type
:
'string'
,
widget
:
'percentSlider'
,
},
labelWidth
:
{
title
:
'标签宽度'
,
description
:
'默认值110'
,
default
:
110
,
type
:
'number'
,
widget
:
'slider'
,
max
:
400
,
props
:
{
hideNumber
:
true
,
},
},
},
}
]
const
settings
=
[
...
...
src/core/widgets/advanced/IDCard/index.js
0 → 100644
View file @
d1bfa69c
import
React
,
{
useEffect
,
useState
}
from
'react'
;
import
styles
from
'./index.less'
;
import
{
Upload
,
Button
,
message
,
Tabs
,
Form
,
Input
}
from
'antd'
;
import
{
UploadOutlined
,
FileOutlined
}
from
'@ant-design/icons'
;
import
FileViewer
from
'react-file-viewer'
;
import
{
uploadFileUrl
,
downloadFileUrl
,
downloadFile
,
uploadCardFileUrl
}
from
'../../../../apis/process'
;
import
{
filenameVerification
}
from
'../../../../utils'
;
import
Drag
from
'../../../components/Drag'
;
import
*
as
XLSX
from
'xlsx'
;
import
{
convertUrlToBase64
}
from
'../../../../utils/index'
;
const
accepts
=
{
'图片'
:
[
'.bmp'
,
'.gif'
,
'.jpeg'
,
'.tiff'
,
'.png'
,
'.svg'
,
'.jpg'
],
};
const
getFileType
=
(
fileName
)
=>
{
if
(
fileName
)
{
//图片
if
(
fileName
.
includes
(
'jpg'
))
{
return
'jpg'
;
}
if
(
fileName
.
includes
(
'png'
))
{
return
'png'
;
}
if
(
fileName
.
includes
(
'svg'
))
{
return
'svg'
;
}
if
(
fileName
.
includes
(
'jpeg'
))
{
return
'jpeg'
;
}
}
return
null
;
};
const
IDCard
=
(
props
)
=>
{
const
site
=
window
.
globalConfig
?.
userInfo
?.
site
||
window
.
globalConfig
?.
userInfo
?.
LocalSite
;
const
{
addons
,
value
,
schema
,
onChange
}
=
props
;
const
{
disabled
,
fileType
,
presetValue
,
placeholder
,
preview
,
download
}
=
schema
;
const
[
showList
,
setShowList
]
=
useState
([]);
const
[
visible
,
setVisible
]
=
useState
(
false
);
const
[
showFile
,
setShowFile
]
=
useState
({
fileType
:
''
,
filePath
:
''
});
const
[
workbook
,
setWorkbook
]
=
useState
({
SheetNames
:
[]
});
const
[
cardSide
,
setCardSide
]
=
useState
(
"front"
);
const
[
cardInfo
,
setCardInfo
]
=
useState
({});
const
showField
=
schema
.
showField
||
[
'身份号码'
,
'姓名'
,
'名族'
,
'住址'
,
'出生'
,
'性别'
,
'失效日期'
,
'签发机关'
,
'签发日期'
];
const
showType
=
fileType
===
'图片'
?
'picture-card'
:
'picture'
;
const
option
=
{
name
:
'file'
,
action
:
`
${
window
.
location
.
origin
}${
uploadCardFileUrl
}
?cardSide=
${
cardSide
}
&cardType=身份证`
,
listType
:
showType
,
// headers:{
// cardSide: "front"
// },
withCredentials
:
true
,
showUploadList
:
{
showRemoveIcon
:
!
disabled
,
showDownloadIcon
:
download
,
},
beforeUpload
(
file
,
fileList
)
{
/** @Tips: 解决提交文件中存在特殊字符的问题 */
let
_continueUpload
=
true
;
let
_msg
=
{
type
:
'success'
,
content
:
'上传成功!'
,
};
fileList
.
forEach
(
item
=>
{
let
_msgObject
=
filenameVerification
(
item
);
if
(
_msgObject
.
type
===
'error'
)
{
_continueUpload
=
false
;
_msg
=
{
type
:
'error'
,
content
:
'上传失败!文件名不符合规则!'
,
};
}
});
_msg
.
type
===
'error'
?
message
[
_msg
.
type
](
_msg
.
content
)
:
''
;
return
_continueUpload
;
},
onChange
:
({
file
,
fileList
,
event
})
=>
{
// 检验名字,名字不通过不允许显示
if
(
filenameVerification
(
file
).
type
===
'error'
)
return
false
;
// 返回的链接在file.response内;不设置url,预览图表不可点击
if
(
file
.
status
===
'done'
&&
file
.
response
.
code
===
0
)
{
// file.url = `${window.origin}${downloadFileUrl}?filePath=${file.response.data}${site ? `&_site=${site}` : ''}`
file
.
cardInfo
=
file
.
response
.
data
;
const
card
=
JSON
.
parse
(
file
.
response
.
data
);
if
(
cardSide
===
"front"
){
setCardInfo
({...
cardInfo
,
...
card
});
setCardSide
(
"back"
);
}
else
{
setCardInfo
({...
cardInfo
,
...
card
});
setCardSide
(
"front"
);
}
message
.
success
(
'上传成功!'
);
}
else
if
(
file
.
status
===
'done'
&&
file
.
response
.
code
!==
0
)
{
file
.
status
=
'error'
;
message
.
error
(
'上传失败!'
);
}
if
(
Array
.
isArray
(
fileList
))
{
setShowList
(
fileList
);
}
else
{
setShowList
([]);
}
},
onPreview
:
async
(
file
)
=>
{
if
(
!
preview
)
return
message
.
info
(
'该附件禁止预览'
);
let
fileType
=
getFileType
(
file
.
name
);
if
(
fileType
)
{
if
([
'xlsx'
,
'xls'
].
includes
(
fileType
))
{
downloadFile
(
file
.
sourcePath
)
.
then
(
response
=>
response
.
arrayBuffer
())
.
then
(
buffer
=>
{
const
data
=
new
Uint8Array
(
buffer
);
const
workbook
=
XLSX
.
read
(
data
,
{
type
:
'array'
});
setWorkbook
(
workbook
);
workbook
?.
SheetNames
?.
forEach
(
sheetName
=>
{
const
worksheet
=
workbook
.
Sheets
[
sheetName
];
// 将工作表转换为 JSON 对象
const
jsonData
=
XLSX
.
utils
.
sheet_to_json
(
worksheet
,
{
header
:
1
,
defval
:
''
});
console
.
log
(
sheetName
,
jsonData
);
});
});
}
setShowFile
({
name
:
file
.
name
,
fileType
:
fileType
,
filePath
:
file
.
thumbUrl
});
setVisible
(
true
);
}
else
{
message
.
info
(
'不支持该类型预览'
);
}
},
onRemove
:
async
(
file
)
=>
{
// let files = value?.split(",") || [];
// let list = files.filter(v => v !== file.name);
// setShowList(await valueToList(list.join(",")));
},
};
useEffect
(()
=>
{
onChange
(
JSON
.
stringify
(
cardInfo
));
},
[
cardInfo
]);
const
elemet
=
()
=>
{
if
([
'jpg'
,
'png'
,
'svg'
,
'jpeg'
].
includes
(
showFile
.
fileType
))
{
return
(
<
div
style
=
{{
width
:
'100%'
,
height
:
'100%'
,
display
:
'flex'
,
justifyContent
:
'center'
,
alignItems
:
'center'
}}
>
<
img
style
=
{{
maxWidth
:
'100%'
,
maxHeight
:
'100%'
}}
src
=
{
showFile
.
filePath
}
><
/img
>
<
/div
>
);
}
else
if
([
'xlsx'
,
'xls'
].
includes
(
showFile
.
fileType
))
{
return
(
<
div
>
<
Tabs
>
{
workbook
?.
SheetNames
?.
map
(
sheetName
=>
{
const
worksheet
=
workbook
.
Sheets
[
sheetName
];
const
jsonData
=
XLSX
.
utils
.
sheet_to_json
(
worksheet
,
{
header
:
1
,
defval
:
''
});
return
(
<
Tabs
.
TabPane
tab
=
{
sheetName
}
key
=
{
sheetName
}
>
<
div
style
=
{{
width
:
'100%'
,
height
:
'550px'
,
overflow
:
'auto'
}}
>
<
table
cellPadding
=
{
10
}
style
=
{{
width
:
'100%'
,
height
:
'100%'
,
display
:
'block'
,
tableLayout
:
'fixed'
}}
>
{
jsonData
.
map
((
row
,
rowIndex
)
=>
{
let
maxLength
=
0
;
jsonData
.
forEach
((
v
,
i
)
=>
{
if
(
v
.
length
>
maxLength
)
{
maxLength
=
v
.
length
;
}
});
let
width
=
(
100
/
maxLength
)
+
'%'
;
return
(
<
tbody
>
<
tr
>
{
row
.
map
((
col
,
colIndex
)
=>
{
return
(
<
td
colSpan
=
{
row
.
length
===
1
?
maxLength
:
1
}
style
=
{{
border
:
'1px solid #aaa'
,
width
:
width
}}
>
{
row
[
colIndex
]}
<
/td
>
);
})
}
<
/tr
>
<
/tbody
>
);
})
}
<
/table
>
<
/div
>
<
/Tabs.TabPane
>
);
})
}
<
/Tabs
>
<
/div
>
);
}
return
(
<
FileViewer
className
=
'fileViewer'
title
=
'123'
fileType
=
{
showFile
.
fileType
}
filePath
=
{
showFile
.
filePath
}
/
>
);
};
const
iconRender
=
(
file
,
listType
)
=>
{
if
(
listType
!==
'text'
)
{
if
(
fileType
===
'图片'
)
{
return
<
img
src
=
{
file
.
url
}
><
/img>
;
}
let
type
=
getFileType
(
file
.
name
);
if
(
type
)
{
return
<
div
className
=
{
styles
.
iconImg
}
type
=
{
type
}
><
/div>
;
}
else
{
return
<
div
className
=
{
styles
.
iconImg
}
type
=
{
'通用'
}
><
/div>
;
}
}
return
<
FileOutlined
/>
;
};
// const valueToList = async (presetValue) => {
// let fileList = [];
// if (presetValue) {
// let list = presetValue ? presetValue.split(',') : [];
// for (let i = 0; i < list.length; i++) {
// if (list[i]) { // @Tips: 直接过滤掉名字中有异常字符的文件
// let uid = i + '_' + Math.random();
// let _obj = {
// uid: uid,
// name: list[i].split('\\').reverse()[0],
// type: fileType === '图片' ? 'image/jpeg' : 'file',
// status: 'done',
// url: list[i]?.includes('http') ? list[i] : `${window.origin}${downloadFileUrl}?filePath=${list[i]}${site ? `&_site=${site}` : ''}`,
// sourcePath: list[i],
// // thumbUrl: list[i]?.includes('http') ? list[i] : (list[i].includes('svg') ? await convertUrlToBase64(`${window.origin}${downloadFileUrl}?filePath=${list[i]}${site ? `&_site=${site}` : ''}`) : `${window.origin}${downloadFileUrl}?filePath=${list[i]}${site ? `&_site=${site}` : ''}`),
// originFileObj: { "uid": uid },
// "response": { "code": 0, "msg": "Ok", "data": list[i], "stackTrace": null },
// "xhr": {},
// };
// if (fileType === '图片') {
// _obj.thumbUrl = list[i]?.includes('http') ? list[i] : (list[i].includes('svg') ? await convertUrlToBase64(`${window.origin}${downloadFileUrl}?filePath=${list[i]}${site ? `&_site=${site}` : ''}`) : `${window.origin}${downloadFileUrl}?filePath=${list[i]}${site ? `&_site=${site}` : ''}`);
// }
// fileList.push(_obj);
// }
// }
// }
// return fileList;
// };
const
setList
=
async
()
=>
{
setCardInfo
(
JSON
.
parse
(
presetValue
));
};
useEffect
(()
=>
{
if
(
presetValue
&&
presetValue
!==
""
){
setList
();
}
},
[
presetValue
]);
return
(
<
div
className
=
{
fileType
===
'图片'
?
styles
.
uploadBoxImg
:
styles
.
uploadBox
}
>
<
Upload
multiple
accept
=
{
accepts
[
fileType
]}
fileList
=
{
showList
}
className
=
"upload-list-inline"
iconRender
=
{
iconRender
}
showUploadList
=
{
false
}
{...
option
}
>
{
disabled
?
null
:
<
Button
icon
=
{
<
UploadOutlined
/>
}
>
{
placeholder
||
(
cardSide
===
"front"
?
'上传正面'
:
'上传背面'
)}
<
/Button
>
}
<
/Upload
>
<
Drag
width
=
{
'70%'
}
title
=
{
showFile
.
name
}
visible
=
{
visible
}
onCancel
=
{()
=>
setVisible
(
false
)}
bodyStyle
=
{{
height
:
650
,
overflowY
:
showFile
.
fileType
.
includes
(
'xlsx'
)
?
'none'
:
"auto"
}}
>
{
elemet
()}
<
/Drag
>
<
Form
layout
=
"horizontal"
disabled
=
{
true
}
style
=
{{
maxWidth
:
'1200px'
,
marginTop
:
'10px'
}}
>
<
div
style
=
{{
display
:
'flex'
,
alignItems
:
'center'
,
width
:
'100%'
,
flexWrap
:
'wrap'
}}
>
{
showField
.
map
((
val
,
i
)
=>
{
return
(
<
Form
.
Item
key
=
{
val
}
label
=
{
val
}
style
=
{{
width
:
'33%'
}}
><
Input
disabled
style
=
{{
width
:
'200px'
}}
value
=
{
cardInfo
[
val
]}
/></
Form
.
Item
>
);
})
}
<
/div
>
<
/Form
>
<
/div
>
);
};
export
default
IDCard
;
\ No newline at end of file
src/core/widgets/advanced/IDCard/index.less
0 → 100644
View file @
d1bfa69c
@import '~antd/es/style/themes/default.less';
@imgSrc: '../../../../assets/images/file';
.uploadBox,
.uploadBoxImg {
.iconImg {
width: 45px;
height: 45px;
margin: 0 auto;
&[type='通用'] {
background: url('@{imgSrc}/通用.png');
background-size: 100% 100%;
}
&[type='jpg'] {
background: url('@{imgSrc}/JPG.png');
background-size: 100% 100%;
}
&[type='png'] {
background: url('@{imgSrc}/PNG.png');
background-size: 100% 100%;
}
&[type="mp3"] {
background: url('@{imgSrc}/音乐.png');
background-size: 100% 100%;
}
&[type="mp4"] {
background: url('@{imgSrc}/视频.png');
background-size: 100% 100%;
}
&[type='docx'] {
background: url('@{imgSrc}/Word.png');
background-size: 100% 100%;
}
&[type='xlsx'],
&[type='xls'] {
background: url('@{imgSrc}/Excel.png');
background-size: 100% 100%;
}
&[type='PDF'] {
background: url('@{imgSrc}/PDF.png');
background-size: 100% 100%;
}
}
.@{ant-prefix}-form-item-label {
width: 100px;
text-align: left;
}
.ant-form-item{
margin-bottom: 12px;
}
}
.uploadBoxImg {
.@{ant-prefix}-upload-list-item-name {
display: none !important;
}
}
\ No newline at end of file
src/core/widgets/advanced/index.js
View file @
d1bfa69c
import
RelationForm
from
'./RelationForm'
import
Signature
from
'./Signature'
import
IDCard
from
'./IDCard'
const
advanced
=
{
RelationForm
,
Signature
,
IDCard
,
}
export
default
advanced
\ No newline at end of file
src/core/widgets/settings/groupStyle/CheckBoxGroup/index.js
0 → 100644
View file @
d1bfa69c
import
React
,
{
useEffect
,
useMemo
,
useState
}
from
'react'
;
import
{
Checkbox
,
Divider
}
from
'antd'
;
import
styles
from
'./index.less'
;
const
plainOptions
=
[
'身份号码'
,
'姓名'
,
'名族'
,
'住址'
,
'出生'
,
'性别'
,
'失效日期'
,
'签发机关'
,
'签发日期'
];
const
defaultCheckedList
=
[
'姓名'
,
'名族'
,
'住址'
,
'身份号码'
,
'出生'
,
'性别'
,
'失效日期'
,
'签发机关'
,
'签发日期'
];
const
CheckboxGroup
=
Checkbox
.
Group
;
const
TableNames
=
(
props
)
=>
{
const
{
value
,
schema
,
onChange
}
=
props
;
const
[
checkedList
,
setCheckedList
]
=
useState
((
value
&&
value
.
length
>
0
)
?
value
:
defaultCheckedList
);
const
checkAll
=
plainOptions
.
length
===
checkedList
.
length
;
const
indeterminate
=
checkedList
.
length
>
0
&&
checkedList
.
length
<
plainOptions
.
length
;
const
onChangeOne
=
(
list
)
=>
{
setCheckedList
(
list
);
onChange
(
list
);
};
const
onCheckAllChange
=
(
e
)
=>
{
setCheckedList
(
e
.
target
.
checked
?
plainOptions
:
[]);
onChange
(
e
.
target
.
checked
?
plainOptions
:
[]);
};
return
(
<
div
className
=
{
styles
.
CheckBoxGroup
}
>
<
Checkbox
indeterminate
=
{
indeterminate
}
onChange
=
{
onCheckAllChange
}
checked
=
{
checkAll
}
>
全选
<
/Checkbox
>
<
Divider
/>
<
CheckboxGroup
options
=
{
plainOptions
}
value
=
{
checkedList
}
onChange
=
{
onChangeOne
}
/
>
<
/div
>
);
};
export
default
TableNames
;
\ No newline at end of file
src/core/widgets/settings/groupStyle/CheckBoxGroup/index.less
0 → 100644
View file @
d1bfa69c
@import '~antd/es/style/themes/default.less';
.CheckBoxGroup {
width: 100%;
.ant-checkbox-group-item {
margin-right: 2px;
width: 90px;
}
}
\ No newline at end of file
src/core/widgets/settings/groupStyle/index.js
View file @
d1bfa69c
import
PercentSlider
from
'./PercentSlider'
import
ShowText
from
'./ShowText'
import
Formatting
from
'./Formatting'
import
DecimalDigits
from
'./DecimalDigits'
import
FileTypeSelect
from
'./FileTypeSelect'
import
ContentHTML
from
'./ContentHTML'
import
StyleHTML
from
'./StyleHTML'
import
RadioGroupW
from
'./RadioGroupW'
import
PercentSlider
from
'./PercentSlider'
;
import
ShowText
from
'./ShowText'
;
import
Formatting
from
'./Formatting'
;
import
DecimalDigits
from
'./DecimalDigits'
;
import
FileTypeSelect
from
'./FileTypeSelect'
;
import
ContentHTML
from
'./ContentHTML'
;
import
StyleHTML
from
'./StyleHTML'
;
import
RadioGroupW
from
'./RadioGroupW'
;
import
CheckBoxGroup
from
'./CheckBoxGroup'
;
const
groupStyle
=
{
PercentSlider
,
...
...
@@ -16,6 +17,7 @@ const groupStyle = {
ContentHTML
,
StyleHTML
,
RadioGroupW
,
}
CheckBoxGroup
,
};
export
default
groupStyle
\ No newline at end of file
export
default
groupStyle
;
\ No newline at end of file
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