任务分组管理,修改为执行器管理
This commit is contained in:
parent
204d7848fe
commit
087cd1d435
|
@ -204,11 +204,12 @@ CREATE TABLE XXL_JOB_QRTZ_TRIGGER_REGISTRY (
|
|||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE XXL_JOB_QRTZ_TRIGGER_GROUP (
|
||||
`group_name` varchar(255) NOT NULL,
|
||||
`group_desc` varchar(255) NOT NULL,
|
||||
`order` int(11) NOT NULL,
|
||||
PRIMARY KEY (`group_name`)
|
||||
`app_name` varchar(64) NOT NULL,
|
||||
`title` varchar(12) NOT NULL,
|
||||
`order` tinyint(4) NOT NULL,
|
||||
PRIMARY KEY (`app_name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
INSERT INTO XXL_JOB_QRTZ_TRIGGER_GROUP VALUES ('xxl-job-executor-example', '示例项目', '1');
|
||||
|
||||
commit;
|
||||
|
||||
|
|
|
@ -2,8 +2,11 @@ package com.xxl.job.admin.controller;
|
|||
|
||||
import com.xxl.job.admin.core.model.ReturnT;
|
||||
import com.xxl.job.admin.core.model.XxlJobGroup;
|
||||
import com.xxl.job.admin.core.thread.JobRegistryHelper;
|
||||
import com.xxl.job.admin.dao.IXxlJobGroupDao;
|
||||
import com.xxl.job.admin.dao.IXxlJobInfoDao;
|
||||
import com.xxl.job.core.registry.RegistHelper;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
|
@ -30,6 +33,14 @@ public class JobGroupController {
|
|||
@RequestMapping
|
||||
public String index(Model model) {
|
||||
List<XxlJobGroup> list = xxlJobGroupDao.findAll();
|
||||
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
for (XxlJobGroup group: list) {
|
||||
List<String> registryList = JobRegistryHelper.discover(RegistHelper.RegistType.EXECUTOR.name(), group.getAppName());
|
||||
group.setRegistryList(registryList);
|
||||
}
|
||||
}
|
||||
|
||||
model.addAttribute("list", list);
|
||||
return "jobgroup/jobgroup.index";
|
||||
}
|
||||
|
@ -39,17 +50,20 @@ public class JobGroupController {
|
|||
public ReturnT<String> save(XxlJobGroup xxlJobGroup){
|
||||
|
||||
// valid
|
||||
if (xxlJobGroup.getGroupName()==null || StringUtils.isBlank(xxlJobGroup.getGroupName())) {
|
||||
return new ReturnT<String>(500, "请输入分组");
|
||||
if (xxlJobGroup.getAppName()==null || StringUtils.isBlank(xxlJobGroup.getAppName())) {
|
||||
return new ReturnT<String>(500, "请输入AppName");
|
||||
}
|
||||
if (xxlJobGroup.getGroupDesc()==null || StringUtils.isBlank(xxlJobGroup.getGroupDesc())) {
|
||||
return new ReturnT<String>(500, "请输入描述");
|
||||
if (xxlJobGroup.getAppName().length()>64) {
|
||||
return new ReturnT<String>(500, "AppName长度限制为4~64");
|
||||
}
|
||||
if (xxlJobGroup.getTitle()==null || StringUtils.isBlank(xxlJobGroup.getTitle())) {
|
||||
return new ReturnT<String>(500, "请输入名称");
|
||||
}
|
||||
|
||||
// check repeat
|
||||
XxlJobGroup group = xxlJobGroupDao.load(xxlJobGroup.getGroupName());
|
||||
XxlJobGroup group = xxlJobGroupDao.load(xxlJobGroup.getAppName());
|
||||
if (group!=null) {
|
||||
return new ReturnT<String>(500, "分组已存在, 请勿重复添加");
|
||||
return new ReturnT<String>(500, "AppName对应的执行器已存在, 请勿重复添加");
|
||||
}
|
||||
|
||||
int ret = xxlJobGroupDao.save(xxlJobGroup);
|
||||
|
@ -59,21 +73,37 @@ public class JobGroupController {
|
|||
@RequestMapping("/update")
|
||||
@ResponseBody
|
||||
public ReturnT<String> update(XxlJobGroup xxlJobGroup){
|
||||
// valid
|
||||
if (xxlJobGroup.getAppName()==null || StringUtils.isBlank(xxlJobGroup.getAppName())) {
|
||||
return new ReturnT<String>(500, "请输入AppName");
|
||||
}
|
||||
if (xxlJobGroup.getAppName().length()>64) {
|
||||
return new ReturnT<String>(500, "AppName长度限制为4~64");
|
||||
}
|
||||
if (xxlJobGroup.getTitle()==null || StringUtils.isBlank(xxlJobGroup.getTitle())) {
|
||||
return new ReturnT<String>(500, "请输入名称");
|
||||
}
|
||||
|
||||
int ret = xxlJobGroupDao.update(xxlJobGroup);
|
||||
return (ret>0)?ReturnT.SUCCESS:ReturnT.FAIL;
|
||||
}
|
||||
|
||||
@RequestMapping("/remove")
|
||||
@ResponseBody
|
||||
public ReturnT<String> remove(String groupName){
|
||||
public ReturnT<String> remove(String appName){
|
||||
|
||||
// valid
|
||||
int count = xxlJobInfoDao.pageListCount(0, 10, groupName, null);
|
||||
int count = xxlJobInfoDao.pageListCount(0, 10, appName, null);
|
||||
if (count > 0) {
|
||||
return new ReturnT<String>(500, "该分组使用中, 不可删除");
|
||||
}
|
||||
|
||||
int ret = xxlJobGroupDao.remove(groupName);
|
||||
List<XxlJobGroup> allList = xxlJobGroupDao.findAll();
|
||||
if (allList.size() == 1) {
|
||||
return new ReturnT<String>(500, "删除失败, 系统需要至少预留一个默认分组");
|
||||
}
|
||||
|
||||
int ret = xxlJobGroupDao.remove(appName);
|
||||
return (ret>0)?ReturnT.SUCCESS:ReturnT.FAIL;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,28 +1,33 @@
|
|||
package com.xxl.job.admin.core.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by xuxueli on 16/9/30.
|
||||
*/
|
||||
public class XxlJobGroup {
|
||||
|
||||
private String groupName;
|
||||
private String groupDesc;
|
||||
private String appName;
|
||||
private String title;
|
||||
private int order;
|
||||
|
||||
public String getGroupName() {
|
||||
return groupName;
|
||||
// registry list
|
||||
private List<String> registryList;
|
||||
|
||||
public String getAppName() {
|
||||
return appName;
|
||||
}
|
||||
|
||||
public void setGroupName(String groupName) {
|
||||
this.groupName = groupName;
|
||||
public void setAppName(String appName) {
|
||||
this.appName = appName;
|
||||
}
|
||||
|
||||
public String getGroupDesc() {
|
||||
return groupDesc;
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setGroupDesc(String groupDesc) {
|
||||
this.groupDesc = groupDesc;
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
|
@ -32,4 +37,12 @@ public class XxlJobGroup {
|
|||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public List<String> getRegistryList() {
|
||||
return registryList;
|
||||
}
|
||||
|
||||
public void setRegistryList(List<String> registryList) {
|
||||
this.registryList = registryList;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,12 +42,12 @@ public class JobRegistryHelper {
|
|||
if (list != null) {
|
||||
for (XxlJobRegistry item: list) {
|
||||
String groupKey = makeGroupKey(item.getRegistryGroup(), item.getRegistryKey());
|
||||
List<String> dataSet = temp.get(groupKey);
|
||||
if (dataSet == null) {
|
||||
dataSet = new ArrayList<String>();
|
||||
List<String> registryList = temp.get(groupKey);
|
||||
if (registryList == null) {
|
||||
registryList = new ArrayList<String>();
|
||||
}
|
||||
dataSet.add(item.getRegistryValue());
|
||||
temp.put(groupKey, dataSet);
|
||||
registryList.add(item.getRegistryValue());
|
||||
temp.put(groupKey, registryList);
|
||||
}
|
||||
}
|
||||
registMap = temp;
|
||||
|
|
|
@ -15,7 +15,7 @@ public interface IXxlJobGroupDao {
|
|||
|
||||
public int update(XxlJobGroup xxlJobGroup);
|
||||
|
||||
public int remove(String groupName);
|
||||
public int remove(String appName);
|
||||
|
||||
public XxlJobGroup load(String groupName);
|
||||
public XxlJobGroup load(String appName);
|
||||
}
|
||||
|
|
|
@ -33,13 +33,13 @@ public class XxlJobGroupDaoImpl implements IXxlJobGroupDao {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int remove(String groupName) {
|
||||
return sqlSessionTemplate.delete("XxlJobGroupMapper.remove", groupName);
|
||||
public int remove(String appName) {
|
||||
return sqlSessionTemplate.delete("XxlJobGroupMapper.remove", appName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XxlJobGroup load(String groupName) {
|
||||
return sqlSessionTemplate.selectOne("XxlJobGroupMapper.load", groupName);
|
||||
public XxlJobGroup load(String appName) {
|
||||
return sqlSessionTemplate.selectOne("XxlJobGroupMapper.load", appName);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -4,14 +4,14 @@
|
|||
<mapper namespace="XxlJobGroupMapper">
|
||||
|
||||
<resultMap id="XxlJobGroup" type="com.xxl.job.admin.core.model.XxlJobGroup" >
|
||||
<result column="group_name" property="groupName" />
|
||||
<result column="group_desc" property="groupDesc" />
|
||||
<result column="app_name" property="appName" />
|
||||
<result column="title" property="title" />
|
||||
<result column="order" property="order" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
t.group_name,
|
||||
t.group_desc,
|
||||
t.app_name,
|
||||
t.title,
|
||||
t.order
|
||||
</sql>
|
||||
|
||||
|
@ -22,26 +22,26 @@
|
|||
</select>
|
||||
|
||||
<insert id="save" parameterType="java.util.Map" >
|
||||
INSERT INTO XXL_JOB_QRTZ_TRIGGER_GROUP ( `group_name`, `group_desc`, `order`)
|
||||
values ( #{groupName}, #{groupDesc}, #{order});
|
||||
INSERT INTO XXL_JOB_QRTZ_TRIGGER_GROUP ( `app_name`, `title`, `order`)
|
||||
values ( #{appName}, #{title}, #{order});
|
||||
</insert>
|
||||
|
||||
<update id="update" parameterType="java.util.Map" >
|
||||
UPDATE XXL_JOB_QRTZ_TRIGGER_GROUP
|
||||
SET `group_desc` = #{groupDesc},
|
||||
SET `title` = #{title},
|
||||
`order` = #{order}
|
||||
WHERE group_name = #{groupName}
|
||||
WHERE app_name = #{appName}
|
||||
</update>
|
||||
|
||||
<delete id="remove" parameterType="java.lang.String" >
|
||||
DELETE FROM XXL_JOB_QRTZ_TRIGGER_GROUP
|
||||
WHERE group_name = #{groupName}
|
||||
WHERE app_name = #{appName}
|
||||
</delete>
|
||||
|
||||
<select id="load" parameterType="java.lang.String" resultMap="XxlJobGroup">
|
||||
SELECT <include refid="Base_Column_List" />
|
||||
FROM XXL_JOB_QRTZ_TRIGGER_GROUP AS t
|
||||
WHERE t.group_name = #{groupName}
|
||||
WHERE t.app_name = #{appName}
|
||||
</select>
|
||||
|
||||
</mapper>
|
|
@ -85,8 +85,8 @@
|
|||
<li class="header">常用模块</li>
|
||||
<li class="nav-click" ><a href="${request.contextPath}/jobinfo"><i class="fa fa-circle-o text-red"></i> <span>调度管理</span></a></li>
|
||||
<li class="nav-click" ><a href="${request.contextPath}/joblog"><i class="fa fa-circle-o text-yellow"></i><span>调度日志</span></a></li>
|
||||
<li class="nav-click" ><a href="${request.contextPath}/jobgroup"><i class="fa fa-circle-o text-red"></i> <span>分组管理</span></a></li>
|
||||
<li class="nav-click" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-yellow"></i><span>使用教程</span></a></li>
|
||||
<li class="nav-click" ><a href="${request.contextPath}/jobgroup"><i class="fa fa-circle-o text-aqua"></i> <span>执行器管理</span></a></li>
|
||||
<li class="nav-click" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-red"></i><span>使用教程</span></a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<!-- /.sidebar -->
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<div class="content-wrapper">
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>任务调度中心<small>使用教程</small></h1>
|
||||
<h1>使用教程<small>任务调度中心</small></h1>
|
||||
<!--
|
||||
<ol class="breadcrumb">
|
||||
<li><a><i class="fa fa-dashboard"></i>调度中心</a></li>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<div class="content-wrapper">
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>分组管理<small>任务调度中心</small></h1>
|
||||
<h1>执行器管理<small>任务调度中心</small></h1>
|
||||
</section>
|
||||
|
||||
<!-- Main content -->
|
||||
|
@ -30,16 +30,17 @@
|
|||
<div class="col-xs-12">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">分组管理</h3>
|
||||
<button class="btn btn-success btn-xs pull-left2 add" >+新增分组</button>
|
||||
<h3 class="box-title">执行器列表</h3>
|
||||
<button class="btn btn-info btn-xs pull-left2 add" >+新增执行器</button>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table id="joblog_list" class="table table-bordered table-striped display" width="100%" >
|
||||
<thead>
|
||||
<tr>
|
||||
<th name="groupDesc" >名称</th>
|
||||
<th name="groupName" >AppName</th>
|
||||
<th name="appName" >AppName</th>
|
||||
<th name="title" >名称</th>
|
||||
<th name="order" >排序</th>
|
||||
<th name="registryList" >OnLine</th>
|
||||
<th name="operate" >操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -47,12 +48,13 @@
|
|||
<#if list?exists && list?size gt 0>
|
||||
<#list list as group>
|
||||
<tr>
|
||||
<td>${group.groupDesc}</td>
|
||||
<td>${group.groupName}</td>
|
||||
<td>${group.appName}</td>
|
||||
<td>${group.title}</td>
|
||||
<td>${group.order}</td>
|
||||
<td><#if group.registryList?exists><#list group.registryList as item><span class="badge bg-green">${item}</span><br></#list></#if></td>
|
||||
<td>
|
||||
<button class="btn btn-warning btn-xs update" groupName="${group.groupName}" groupDesc="${group.groupDesc}" order="${group.order}" >编辑</button>
|
||||
<button class="btn btn-danger btn-xs remove" groupName="${group.groupName}" >删除</button>
|
||||
<button class="btn btn-warning btn-xs update" appName="${group.appName}" title="${group.title}" order="${group.order}" >编辑</button>
|
||||
<button class="btn btn-danger btn-xs remove" appName="${group.appName}" >删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</#list>
|
||||
|
@ -71,17 +73,17 @@
|
|||
<div class="modal-dialog ">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" >新增分组</h4>
|
||||
<h4 class="modal-title" >新增执行器</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-horizontal form" role="form" >
|
||||
<div class="form-group">
|
||||
<label for="lastname" class="col-sm-2 control-label">分组<font color="red">*</font></label>
|
||||
<div class="col-sm-10"><input type="text" class="form-control" name="groupName" placeholder="请输入“分组”" maxlength="200" ></div>
|
||||
<label for="lastname" class="col-sm-2 control-label">AppName<font color="red">*</font></label>
|
||||
<div class="col-sm-10"><input type="text" class="form-control" name="appName" placeholder="请输入“AppName”" maxlength="64" ></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="lastname" class="col-sm-2 control-label">描述<font color="red">*</font></label>
|
||||
<div class="col-sm-10"><input type="text" class="form-control" name="groupDesc" placeholder="请输入“描述”" maxlength="200" ></div>
|
||||
<label for="lastname" class="col-sm-2 control-label">名称<font color="red">*</font></label>
|
||||
<div class="col-sm-10"><input type="text" class="form-control" name="title" placeholder="请输入“名称”" maxlength="12" ></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="lastname" class="col-sm-2 control-label">排序<font color="red">*</font></label>
|
||||
|
@ -105,17 +107,17 @@
|
|||
<div class="modal-dialog ">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" >编辑分组</h4>
|
||||
<h4 class="modal-title" >编辑执行器</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-horizontal form" role="form" >
|
||||
<div class="form-group">
|
||||
<label for="lastname" class="col-sm-2 control-label">分组<font color="red">*</font></label>
|
||||
<div class="col-sm-10"><input type="text" class="form-control" name="groupName" placeholder="请输入“分组”" maxlength="200" readonly ></div>
|
||||
<label for="lastname" class="col-sm-2 control-label">AppName<font color="red">*</font></label>
|
||||
<div class="col-sm-10"><input type="text" class="form-control" name="appName" placeholder="请输入“AppName”" maxlength="64" readonly ></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="lastname" class="col-sm-2 control-label">描述<font color="red">*</font></label>
|
||||
<div class="col-sm-10"><input type="text" class="form-control" name="groupDesc" placeholder="请输入“描述”" maxlength="200" ></div>
|
||||
<label for="lastname" class="col-sm-2 control-label">名称<font color="red">*</font></label>
|
||||
<div class="col-sm-10"><input type="text" class="form-control" name="title" placeholder="请输入“名称”" maxlength="12" ></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="lastname" class="col-sm-2 control-label">排序<font color="red">*</font></label>
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<div class="content-wrapper">
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>调度管理任务调度中心<small></small></h1>
|
||||
<h1>调度管理<small>任务调度中心</small></h1>
|
||||
<!--
|
||||
<ol class="breadcrumb">
|
||||
<li><a><i class="fa fa-dashboard"></i>调度管理</a></li>
|
||||
|
|
|
@ -2,21 +2,25 @@ $(function() {
|
|||
|
||||
// remove
|
||||
$('.remove').on('click', function(){
|
||||
var groupName = $(this).attr('groupName');
|
||||
var appName = $(this).attr('appName');
|
||||
|
||||
ComConfirm.show("确认删除分组?", function(){
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : base_url + '/jobgroup/remove',
|
||||
data : {"groupName":groupName},
|
||||
data : {"appName":appName},
|
||||
dataType : "json",
|
||||
success : function(data){
|
||||
if (data.code == 200) {
|
||||
ComAlert.show(1, '删除成功');
|
||||
window.location.reload();
|
||||
} else {
|
||||
if (data.msg) {
|
||||
ComAlert.show(2, data.msg);
|
||||
} else {
|
||||
ComAlert.show(2, '删除失败');
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -37,12 +41,12 @@ $(function() {
|
|||
errorClass : 'help-block',
|
||||
focusInvalid : true,
|
||||
rules : {
|
||||
groupName : {
|
||||
appName : {
|
||||
required : true,
|
||||
rangelength:[4,200],
|
||||
rangelength:[4,64],
|
||||
myValid01 : true
|
||||
},
|
||||
groupDesc : {
|
||||
title : {
|
||||
required : true,
|
||||
rangelength:[4, 12]
|
||||
},
|
||||
|
@ -53,13 +57,13 @@ $(function() {
|
|||
}
|
||||
},
|
||||
messages : {
|
||||
groupName : {
|
||||
required :"请输入“分组”",
|
||||
rangelength:"长度限制为4~200",
|
||||
appName : {
|
||||
required :"请输入“AppName”",
|
||||
rangelength:"AppName长度限制为4~64",
|
||||
myValid01: "限制以小写字母开头,由小写字母、数字和中划线组成"
|
||||
},
|
||||
groupDesc : {
|
||||
required :"请输入“描述”",
|
||||
title : {
|
||||
required :"请输入“执行器名称”",
|
||||
rangelength:"长度限制为4~12"
|
||||
},
|
||||
order : {
|
||||
|
@ -104,8 +108,8 @@ $(function() {
|
|||
});
|
||||
|
||||
$('.update').on('click', function(){
|
||||
$("#updateModal .form input[name='groupName']").val($(this).attr("groupName"));
|
||||
$("#updateModal .form input[name='groupDesc']").val($(this).attr("groupDesc"));
|
||||
$("#updateModal .form input[name='appName']").val($(this).attr("appName"));
|
||||
$("#updateModal .form input[name='title']").val($(this).attr("title"));
|
||||
$("#updateModal .form input[name='order']").val($(this).attr("order"));
|
||||
|
||||
$('#updateModal').modal({backdrop: false, keyboard: false}).modal('show');
|
||||
|
@ -115,12 +119,12 @@ $(function() {
|
|||
errorClass : 'help-block',
|
||||
focusInvalid : true,
|
||||
rules : {
|
||||
groupName : {
|
||||
appName : {
|
||||
required : true,
|
||||
rangelength:[4,200],
|
||||
rangelength:[4,64],
|
||||
myValid01 : true
|
||||
},
|
||||
groupDesc : {
|
||||
title : {
|
||||
required : true,
|
||||
rangelength:[4, 12]
|
||||
},
|
||||
|
@ -131,13 +135,13 @@ $(function() {
|
|||
}
|
||||
},
|
||||
messages : {
|
||||
groupName : {
|
||||
required :"请输入“分组”",
|
||||
rangelength:"长度限制为4~200",
|
||||
appName : {
|
||||
required :"请输入“AppName”",
|
||||
rangelength:"AppName长度限制为4~64",
|
||||
myValid01: "限制以小写字母开头,由小写字母、数字和中划线组成"
|
||||
},
|
||||
groupDesc : {
|
||||
required :"请输入“描述”",
|
||||
title : {
|
||||
required :"请输入“执行器名称”",
|
||||
rangelength:"长度限制为4~12"
|
||||
},
|
||||
order : {
|
||||
|
|
Loading…
Reference in New Issue