Commit d2c2916a authored by zihao21's avatar zihao21

Merge origin/master into master

parents 299727c3 fcc1f11d
......@@ -130,82 +130,3 @@ if(user.getStatus() == EXPIRED_USER_STATUS){
- `public SysRole updateSysRole(SysRole role)`
- `public int updateSysUsers(List<SysUser> users)`
--- ---
## [三、前端开发规范](id:frontEndDev)
#### 目录划分
按照web目录规范,前端文件存放在如下目录
```
webapp
├─lib
│ ├─bootstrap-3.3.7
│ ├─font-awesome-4.6.3
│ └─kendoui
├─resources
│ ├─css
│ ├─font
│ ├─images
│ ├─js
│ ├─upload
└─WEB-INF
├─web.xml
└─view
```
资源文件目录
资源文件按照类型划分为两个目录:**`lib`****`resources`**
- **`lib`**目录存放kendoui的所有文件
- **`resources`**目录存放程序通用的资源文件
功能文件目录
功能文件存放在**`view`**目录下,目录的命名规则按照
> **[模块代码]**/**[功能项代码]**
例如:用户管理的功能代码是SYS001,那么用户管理相关的界面文件都存放在
```
src/main/webapp/WEB-INF/view/sys/sys001/
```
--- ---
## [四、前端命名规范](id:frontEndName)
#### 文件命名约定
界面文件按照如下格式:
>[模块代码]<u> </u>[业务对象]<u> </u>[功能操作].html
例如:用户查询界面: sys_user_query.html
#### url命名约定
对于系统中提交的url地址,按照以下格式命名
>[contextPath]/[模块代码]/[业务对象]/[操作类型]
对于常见的几种操作类型定义如下
查询URL
>[模块代码]/[业务对象]/query
批量提交
>[模块代码]/[业务对象]/submit
批量删除
> [模块代码]/[业务对象]/remove
例如用户的查询: sys/user/query
#### 脚本变量
变量命名原则是减少变量冲突,采用Camel命名法。
>var [开发项ID]_[具体业务含义] = {}
例如:用户管理界面中用户查询的grid数据
>var d_um_002_grid = $('#d_um_002_grid').data("kendoGrid");
#### 脚本函数名
函数命名也采用Camel命名法.
>function [开发项ID]_[具体业务含义](){...}
例如用户查询函数
```javascript
function d_um_002_query(){
....
}
## [一、前端开发规范](id:frontEndDev)
#### 目录划分
按照web目录规范,前端文件存放在如下目录
```
webapp
├─resources
│ ├─css
│ ├─images
│ ├─js
└─WEB-INF
├─web.xml
└─view
```
资源文件目录
资源文件按照类型划分为:**`resources`**
- **`resources`**目录存放程序通用的资源文件
功能文件目录
功能文件存放在**`view`**目录下,目录的命名规则按照
> **[模块代码]**/**[功能项代码]**
例如:用户管理的功能代码是SYS001,那么用户管理相关的界面文件都存放在
```
src/main/webapp/WEB-INF/view/sys/sys001/
```
--- ---
## [二、前端命名规范](id:frontEndName)
#### 文件命名约定
界面文件按照如下格式:
>[模块代码]<u> </u>[业务对象]<u> </u>[功能操作].html
例如:用户查询界面: sys_user_query.html
#### url命名约定
对于系统中提交的url地址,按照以下格式命名
>[contextPath]/[模块代码]/[业务对象]/[操作类型]
对于常见的几种操作类型定义如下
查询URL
>[模块代码]/[业务对象]/query
批量提交
>[模块代码]/[业务对象]/submit
批量删除
> [模块代码]/[业务对象]/remove
例如用户的查询: sys/user/query
#### 脚本变量
变量命名原则是减少变量冲突,采用Camel命名法。
>var [开发项ID]_[具体业务含义] = {}
例如:用户管理界面中用户查询的grid数据
>var d_um_002_grid = $('#d_um_002_grid').data("kendoGrid");
#### 脚本函数名
函数命名也采用Camel命名法.
>function [开发项ID]_\[具体业务含义\](){...}
例如用户查询函数
```javascript
function d_um_002_query(){
....
}
\ No newline at end of file
# hlsCheckbox 标签
hlsCheckbox框,一般与<h:hlsForm><h:hlsHBox>一起使用,定义一个带文字说明的选择框。
```xml
<h:hlsCheckbox id="testCheckbox" bind="value:data.testValue" readonly="false" checkedValue="Y" uncheckedValue="N" />
```
### **主要属性**
| 属性名 | 类型 | 描述 |
| --- | --- | --- |
|promptColspan | String | 文字说明盒子的长度|
|promptClassName | String | 文字说明盒子的样式|
|promptImage|String|文字说明部分的图标(要求是一个完整的img标签)|
| prompt | String | 文字说明 |
| bind | String | 该选择框绑定关系 |
| required | String & Boolean & 不填 | 必选|
| checkedValue | String | 选中时input框的值 |
| uncheckedValue | String | 未选中时input框的值 |
| readonly | Boolean | 是否是可点击的 |
### **用法示例**
```xml
var viewModel = kendo.observable({
enabled: true,
isEnabled: false,
data: {},
});
<h:hlsForm title="hlsFormTitle" id="hlsFormTitleId" width="100%">
<h:hlsHBox>
<h:hlsCheckbox name="testCheckboxName" id="testCheckbox"
bind="enabled: enabled,value:model.testValue"
checked="checked" checkedValue="Y" prompt="hlsCheckbox测试:" uncheckedValue="N"
promptColspan="1" style="width:100%"/>
</h:hlsHBox>
</h:hlsForm>
```
# hlsCombobox标签
ComboBox下拉列表组件,通常数据源以code - meaning形式出现,用户操作的是meaning,实际保存的是code。
```xml
<h:hlsCombobox name="hlsCombobox" id="hlsCombobox"
bind="enabled: isEnabled,source: sdDataSource, value:data.code"
colspan="3"
dataTextField="meaning"
dataValueField="code"
placeholder="hlsCombobox"
prompt="hlsCombobox"
promptColspan="1"
valuePrimitive="true"
style="width:100%"
/>
```
### **主要属性**
属性名 | 类型
-------- | ---
id | String
promptColspan | String
promptClassName | String
promptImage | String
prompt | String
colspan | String
hlsClassName | String
animation | Boolean
autoBind | Boolean
autoWidth | Boolean
cascadeFrom| String
cascadeFromField | String
clearButton| Boolean
dataSource | DataSource
dataTextField | String
dataValueField| String
delay | Number
enable | Boolean
enforceMinLength| Boolean
filter | String
fixedGroupTemplate| String \| Function
footerTemplate| String \| Function
groupTemplate| String \| Function
height| Number
highlightFirst| Boolean
ignoreCase| Boolean
index| Number
minLength| Number
noDataTemplate| String \| Function
placeholder | String
suggest| Boolean
headerTemplate| String \| Function
template| String \| Function
text| String
value| String
valuePrimitive| Boolean
virtual| Boolean
change| Function
close| Function
dataBound | Function
filtering| Function
open| Function
select| Function
cascade| Function
> 属性用法请参考KendoUI文档 [http://docs.telerik.com/kendo-ui/api/javascript/ui/combobox](http://docs.telerik.com/kendo-ui/api/javascript/ui/combobox)
### **用法示例*
```javascript
//对于系统定义好的数据源
<script src="${base.contextPath}/common/code?sdDataSource=FND.CODING_RULE_TYPE" type="text/javascript"></script>
//自定义静态数据源sdDataSource
var sdDataSource= new kendo.data.DataSource({
data:["one","two"]
});
//自定义动态查询到的数据源sdDataSource
$.ajax({
type : 'GET',
url : url,
async: false,
contentType : "application/json; charset=utf-8",
success : function(datas) {
viewModel.set("sdDataSource,",datas.rows);
}
});
//需要注意,bind中写dataSource,需要在viewModel中定义
var viewModel = kendo.observable({
enabled: true,
isEnabled: false,
data: {},
sdDataSource:sdDataSource,
});
//下拉列表渲染函数
var itemTemplate = function (){
//...
}
//选中某一个列表选项后触发
function onComboboxChange(e){
var value = this.value();
// Use the value of the widget.
}
//以上两种函数定义方法皆可
```
```xml
<h:hlsCombobox name="hlsCombobox" id="hlsCombobox"
bind="enabled: isEnabled,source: sdDataSource, value:data.code"
colspan="3"
dataTextField="meaning"
dataValueField="code"
placeholder="hlsCombobox"
prompt="hlsCombobox"
promptColspan="1"
valuePrimitive="true"
style="width:100
change="onComboboxChange"
template="fn:itemTemplate"
/>
```
#### 特别注意 valuePrimitive="true" 如果该值为false,选中的值将不会匹配给dataTextField和dataValueField
dataValueField
> **提示:** 某些属性类型为**String \| Function** 时,表明它可以定义为字符串或者函数。因此当需要定义为Function时需要增加前缀**fn:**来区分(例如函数template="fn:itemTemplate" )
# HlsDateTimePicker标签
时间选择器(带时分秒)
### **主要属性**
属性名 | 类型
-------- | -----
promptColspan | Integer
promptClassName | className
id | String
promptImage | String
prompt | String
colspan | String
hlsClassName | String
animation | Boolean
ARIATemplate| String
culture | String
depth| String
footer| String & Function
format| String
max| String
min| String
start| String
value | String
name | String
bind | Function
required | Boolean
change | Function
close| Function
open | Function
placeholder | String
interval |Integer
timeFormat |String
> **提示:** 属性用法请参考kendoui API文档 http://docs.telerik.com/kendo-ui/api/javascript/ui/datetimepicker
### **用法示例**
```javascript
//注意是写在script标签内的
var viewModel = kendo.observable({
data:{},
isEnabled:true
});
function open(){
//...
}
function change(e){
//...
}
```
```xml
<h:hlsDateTimePicker name="hlsDateTimePicker" id="hlsDateTimePicker"
colspan="3"
bind="enabled:isEnabled,value:data.hlsDateTimePicker"
prompt="上会时间"
promptColspan="1"
required="true"
style="width:100%"
max="2016-6-6"
open="open"
/>
id:可写可不写
bind:属性里面包含配置两个选项:
(1)enabled配置日期选择器是否可以编辑,值为Boolean类型的变量,在viewModel里面配置
(2)value配置日期选择器与变量关联,默认是在ViewModel下面的变量
max:可选择日期的上限值
style可改变该控件的样式
open:点击控件调用的方法
change:选择日期之后的事件
prompt:为控件添加一个label,该属性一般和<h:hlsForm><h:hlsHBox>一起使用
colspan:时间选择器的宽度(按照bootstrap,列12等分来计算)
promptColspan:label的宽度
```
> **提示:**
> - 上述所有属性直接添加在标签内即可,(**[属性名] = "..."**),对于既是**String**类型又是**Function**类型的属性,只需要加**fn:**前缀区分即可,(**fn:函数名**)
> - 对于**max min**等本该是**时间类型**的属性,改为日期字符串,且连接符号为"**-**"
# hlsLOV标签
#### lov标签的数据源和展示由功能定义,因此在写LOV之前需要在lov配置功能处配好相关信息,可配置项包括:
##### 1.lov框的具体样式、结构;
##### 2.lov框的数据来源
```xml
//code、locale、contenxtPath为必须的参数,其中code为所需lov在lov定义时的code的值
<h:hlsLov name="lov" id="lov" code="LOV_CON_CONTRACT_TENANT" contextPath="${base.contextPath}" locale="${base.locale}"/>
```
#### **必须参数**
参数名|类型/值
----|----
code|String
locale|String (一般为${base.locale})
contextPath|String(一般为${base.contextPath})
#### **一般属性**
属性名|类型
----|----
promptColspan | String
promptClassName | String
promptImage | String
prompt | String
name|String
enabled|Boolean
valuePrimitive|Boolean
text|String
dataTextField|String
dataValueField|String
open|function
close|function
select|function
query|function
用法示例:
```javascript
var viewModel = kendo.observable({
enabled: true,
isEnabled: false,
data: {},
});
//以下代码写在script标签内
//lov查询时触发
function lovQuery(e) {
//lov查询拼上条件
e.param['contractId'] = 1237;
};
//选中lov行数据后触发
function lovSelect(e) {
//选中lov行数据后
//options是
viewModel.data.set("contractId", e.item.contractId);
};
```
```xml
<!--xml代码-->
<h:hlsLov name="lov" id="lov" code="LOV_ROLE" colspan="3" contextPath="${base.contextPath}" data-bind="enabled: enabled, locale="${base.locale}" placeholder="承租人名称" prompt="lov:" promptColspan="1" select="lovSelect" query="lovQuery" style="width: 100%"/>
```
> **提示:**
> - 上述所有属性直接添加在标签内即可,(**[属性名] = "..."**),对于既是**String**类型又是**Function**类型的属性,只需要加**fn:**前缀区分即可,(**fn:函数名**)
# hlsToolBar
-------------
hlsToolBar菜单栏标签,通常与<h:hlsButton>一起使用,固定于页面的底部,不随滚动条变化而变化。
##### xml配置
```xml
<!--h:为命名空间,必须要加的;treeList为组件名;id可写可不写-->
<h:hlsToolBar>
<h:hlsButton click="TEST5" text="测试" />
</h:hlsToolBar>
```
#### **一般属性**
> **提示:**
该标记通常作为一个容器,固定于页面底部,一般只对其样式进行修改。
属性名 | 类型
-------- | ---
hlsStyle| String
hlsClassName | String
用法示例:
```javascript
//注意是写在script标签里的内容
function TEST5(){
...
}
```
```xml
<h:hlsToolBar hlsStyleClassName="test-class" hlsStyle="backgroud-color:blue">
<h:hlsButton click="TEST5" text="测试" />
</h:hlsToolBar>
hlsStyleClassName:css类样式名字
hlsStyle:css样式
click:点击事件调用的方法名
text:按钮的文字说明
```
> **提示:**
> - 每个hls开头的标签都有hlsClassName和hlsStyle属性,因为有的标签主要说明其功能,所以没强调。
tabStrip
-------------
#### **tabStrip标签页**
```xml
<!--h:为命名空间,必须要加的;id可写可不写;value为默认打开的标签页,属性值对应tab页的title的值;animation表示是否启用动画,值类型为布尔值-->
<h:tabStrip id="tabstrip" value="Paris" animation="false">
<h:tabs>
//title为标签页的标题
<h:tab title="Paris">
//此标签的html内容
...
</h:tab>
<h:tab title="New York">
...
</h:tab>
</h:tabs>
</h:tabStrip>
```
#### **一般属性**
> **提示:**
> - 属性用法请参考kendoui API文档的tabStrip:http://docs.telerik.com/kendo-ui/api/javascript/ui/tabstrip
属性名|类型
----|----
animation|Boolean
collapsible|Boolean
dataContentField|String
dataSource | kendo.data.DataSource(Function)
dataTextField|String
navigatable|true
scrollable|Boolean
tabPosition|String
value|String
activate|function
contentLoad|function
error|function
select|function
change| Function
dataBound | Function
dataBinding|function
用法示例:
```xml
<h:tabStrip value="标签2" collapsible="true" tabPosition="top">
<h:tabs>
<h:tab title="标签1">
<h:button text="hello1" className="btn-primary" click="test" enable="true" icon="cancel"/>
<h:button text="hello2" className="btn-primary" click="test" enable="true" icon="cancel"/>
</h:tab>
<h:tab title="标签2">
<h:button text="hello3" className="btn-primary" click="test" enable="true" icon="cancel"/>
<h:button text="hello4" className="btn-primary" click="test" enable="true" icon="cancel"/>
</h:tab>
</h:tabs>
</h:tabStrip>
<!--或者 将tabStrip与dataSource绑定,-->
<h:dataSource id="dataSourceTabStrip">
<h:data>
<h:item name="tab1" content="tab1content"/>
<h:item name="tab2" content="tab2content"/>
</h:data>
</h:dataSource>
<h:tabStrip dataSource="dataSourceTabStrip" dataTextField="name" dataContentField="content" />
```
> **提示:**
> - 上述所有属性直接添加在标签内即可,(**[属性名] = "..."**),对于既是**String**类型又是**Function**类型的属性,只需要加**fn:**前缀区分即可,(**fn:函数名**)
hlsForm表单
-----------------
<h:hlsForm>包括两个titile部分和body部分,body通常由<h:hlsBox><h:hlsMaskedTextBox><h:hlshlsNumericTextBox>等标记组成。
#### 一般用法
```html
<!-- hlsForm子标签的使用请参见相应文档,这里不再说明-->
<h:hlsForm title="hlsForm" width="100%">
<h:hlsHBox>
<h:hlsDatePicker name="transactionDate" id="transactionDate" bind="enabled: enabled,value:data.transactionDate" colspan="4" style="width:50%" placeholder="退款日期" prompt="退款日期:" promptColspan="2" required="true"/>
<h:hlsCombobox name="paymentMethod" id="paymentMethod" bind="enabled: enabled, source:cshPayments , value:data.paymentMethod" colspan="4" dataTextField="description" dataValueField="value" placeholder="退款方式" prompt="退款方式:" promptColspan="2" required="true" valuePrimitive="true" style="width:50%"/>
</h:hlsHBox>
<h:hlsHBox>
<h:hlsNumericTextBox name="returnDueAmount" id="returnDueAmount" bind="enabled: enabled, value:data.returnDueAmount" style="width:50%" colspan="4" prompt="退款金额:" promptColspan="2" required="true" validationMessage="Enter {0}"/>
<h:hlsMaskedTextBox name="bankSlipNum" id="bankSlipNum" bind="enabled: enabled, value:data.bankSlipNum" colspan="4" prompt="银行流水号:" promptColspan="2" style="width:50%"/>
</h:hlsHBox>
<h:hlsHBox>
<h:hlsLov name="bankAccountName" id="bankAccountName" code="BANK_ACCOUNT_INFO" colspan="4" contextPath="${base.contextPath}" data-bind="enabled: enabled, value:data.bankAccountId,text:data.bankAccountName" locale="${base.locale}" placeholder="退款账户" prompt="退款账户:" promptColspan="2" query="bankAccountNameQuery" change="bankAccountNameChange" select="bankAccountNameSelect" required="true" style="width:50%" />
<h:hlsMaskedTextBox name="bankAccountNum" id="bankAccountNum" bind="enabled: isEnabled, value:data.bankAccountNum" colspan="4" prompt="账号:" promptColspan="2" style="width:50%" />
</h:hlsHBox>
</h:hlsForm>
```
### **主要属性**
| 属性名 | 类型 | 描述 |
| --- | --- | --- |
| width | String | form组件的宽度|
|hlsClassName | String |组件的类样式|
| title | String | 标题文字|
> **注意:**
```javascript
$('#query-form input').keydown(function (e) {
if (e.keyCode == 13) {
e.target.blur();
viewModel.queryResource(e);
}
});
```
>**详解:**
> - query-form 是表单id,input指表单内所有输入框 (样式选择器选到input)
> - viewModel.queryResource(e)当按下回车按钮时都执行查询方法
---------------
#### viewModel在多处出现,这里粗略的介绍一下
```
viewModel用法
Kendo MVVM是一种MVVM的实现,当然可以跟Kendo 组件(widgets)和数据源(datasource)进行无缝结合
```javascript
var viewModel = kendo.observable({
model: {
//model 中可设置绑定初值
id:5
},
//可以自定义函数方法 :如saveFunction方法
saveFunction: function () {
$('#grid').data('kendoGrid').saveChanges();
},
queryResource: function (e) {
$('#grid').data('kendoGrid').dataSource.page(1);
}
});
```
使用:(**注意data-bind**)
```html
<div id="view">
<input data-bind="value: model.id" />
<button data-bind="click: saveFunction">Display</button>
</div>
```
关于绑定
```html
<div id="view" data-bind="value: model.name"></div>
<script>
var viewModel = kendo.observable({
model: {
name: "John Doe"
}
});
kendo.bind($("#view"), viewModel);
</script>
```
> **注意:**
> - viewModel中绑定是实时的,即viewModel中的name字段发生变化,则input中的value会一起变化,反之,当input中的value值变化,则viewModel中的name字段也会一同发生变化
> - viewModel中的所有字段属性都是可自定义的
> - 绑定需要input中data-bind 和kendo-bind两次绑定 第一是将数值绑定到model上,第二次是将此div绑定到viewModel上,这样才能访问到viewModel的model属性,也就是第一次绑定才有效
重置按钮
```javascript
resetForm : function(e) {
var formData = viewModel.model.toJSON();
for ( var k in formData) {
viewModel.model.set(k, null);
}
}
```
```html
<span class="btn btn-default" data-bind="click:resetForm" type="button"><@spring.message "hap.reset"/></span>
```
> **注意:**
> - viewModel与表单绑定后,model里面的字段变化,input框里的value里一起变化,所以在清空viewModel的同时,清空了表单里的数据
......@@ -10,15 +10,15 @@
计划任务可以定义五种频率,分别是:月、周、日、时、分。
月:可定义每几个月的第几天(可选择多个,使用逗号隔开)执行,同时可以指定执行的具体时分。
* 月:可定义每几个月的第几天(可选择多个,使用逗号隔开)执行,同时可以指定执行的具体时分。
周:可定义每几个周的周几执行,同时可指定执行的具体时分。
* 周:可定义每几个周的周几执行,同时可指定执行的具体时分。
日:可定义每几天执行一次,同时可指定执行的具体时分。
* 日:可定义每几天执行一次,同时可指定执行的具体时分。
时:可定义每几个小时执行一次。
* 时:可定义每几个小时执行一次。
分:可定义每几分钟执行一次。
* 分:可定义每几分钟执行一次。
### 2、任务维护
......@@ -64,7 +64,7 @@ public class demoJob extends AbstractJob{
} else {
setExecutionSummary("执行完成!" );
}
}
@Override
public boolean isRefireImmediatelyWhenException() {
......@@ -79,7 +79,82 @@ public class demoJob extends AbstractJob{
注意:
调用`setExecutionSummary()`方法可以在执行记录中,记录你的业务数据的记录,或者抛出的异常的具体信息。
* 调用`setExecutionSummary()`方法可以在执行记录中,记录你的业务数据的记录,或者抛出的异常的具体信息。
* 在上下文中获取JobDataMap可以拿到你在任务配置中定义的参数
### 3、计划任务工作台
#### 3.1 新建任务
1.点击左上角的新建任务按钮,可以进入以下界面创建一个新的任务:
![](/assets/job_create.png)
此处任务名称必须唯一,在lov中选择对应的任务代码。
2.下拉滚动条,可以看到如下所示的界面:
![](/assets/job_create2.png)
周期性:表示一个cron任务,可以在计划名称的lov中选定自己定义的一个周期,然后计划任务可以根据任务周期定义功能中的执行频率,以及执行的开始和结束时间,进行执行。
制定时间:选择执行的时间,然后计划任务可以根据选中的执行时间,开始执行。
立即执行:可以直接出发你定义的计划任务。
以上三种方式,在定义时只能选择一种,点击保存后,任务会自动开始进行调度。
注意:
* 在集群环境中,请确保各个节点的机器的时间一致,否则会造成job的调度出现问题。
* 在集群环境中,请不要用节点外的机器操作job。
* 在开发环境中,在config.properties中调度器自动启动的状态应该默认设置为false,在正式环境中应该设置为true(设置为true,表示调度器随着服务器启动而启动):
在开发环境中,由于隐藏了调度的启动和关闭按钮,所以如果想打开调度器,必须要手动输入url:/job/scheduler/start,否则你的计划任务将不会执行。
#### 3.2 任务状态
1.任务执行时,状态不同,颜色也不同:
![](/assets/job_status.png)
状态说明:
* 正在执行,颜色显示为绿色。
* 发生异常,颜色显示为红色。
* 任务暂停,颜色显示为橙色。
* 任务完成,颜色显示为灰色。
* 任务阻塞,颜色显示为黑色。
2.想要修改任务的状态,或者删除任务,可以勾选对应的计划任务进行操作:
![](/assets/job_change_status.png)
备注:执行完成的任务无法恢复到执行状态。
### 4、执行记录
#### 4.1 查看任务的执行记录
![](/assets/job_infomation.png)
参数说明:
* 任务名称:计划任务名称
* 任务组:计划任务所属组别
* 任务状态:任务完成状态,正常执行完为FINISH,执行出错为FAILED,任务被禁止为VETOED
* 执行概要:任务执行过程中的信息,如执行结果,异常信息等(既setExecutionSummary方法中所设置的值)。
* 上次执行时间:上一次任务预计执行的时间点。
* 计划执行时间:预计完成上一次任务后,根据指定的执行间隔推算的本次任务执行的时间。
* 下次执行时间:预计执行完本次任务后,根据指定的执行间隔推算的下一次执行时间点。
* 实际执行时间:本次任务实际执行的时间点。
在上下文中获取JobDataMap可以拿到你在任务配置中定义的参数
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment