Merge branch 'master' of https://github.com/metersphere/metersphere
This commit is contained in:
commit
3e0604c62f
|
@ -13,20 +13,19 @@ import io.metersphere.service.SystemParameterService;
|
|||
import io.metersphere.service.UserService;
|
||||
import io.metersphere.track.request.testreview.SaveCommentRequest;
|
||||
import io.metersphere.track.request.testreview.SaveTestCaseReviewRequest;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.flywaydb.core.internal.util.StringUtils;
|
||||
import org.springframework.mail.MailException;
|
||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class MailService {
|
||||
|
@ -38,31 +37,7 @@ public class MailService {
|
|||
private SystemParameterService systemParameterService;
|
||||
|
||||
public void sendHtml(String id, List<Notice> notice, String status, String type) {
|
||||
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
|
||||
List<SystemParameter> paramList = systemParameterService.getParamList(ParamConstants.Classify.MAIL.getValue());
|
||||
javaMailSender.setDefaultEncoding("UTF-8");
|
||||
javaMailSender.setProtocol("smtps");
|
||||
for (SystemParameter p : paramList) {
|
||||
if (p.getParamKey().equals("smtp.host")) {
|
||||
javaMailSender.setHost(p.getParamValue());
|
||||
}
|
||||
if (p.getParamKey().equals("smtp.port")) {
|
||||
javaMailSender.setPort(Integer.parseInt(p.getParamValue()));
|
||||
}
|
||||
if (p.getParamKey().equals("smtp.account")) {
|
||||
javaMailSender.setUsername(p.getParamValue());
|
||||
}
|
||||
if (p.getParamKey().equals("smtp.password")) {
|
||||
javaMailSender.setPassword(EncryptUtils.aesDecrypt(p.getParamValue()).toString());
|
||||
}
|
||||
}
|
||||
Properties props = new Properties();
|
||||
props.put("mail.smtp.auth", "true");
|
||||
props.put("mail.smtp.starttls.enable", "true");
|
||||
props.put("mail.smtp.starttls.required", "true");
|
||||
props.put("mail.smtp.timeout", "30000");
|
||||
props.put("mail.smtp.connectiontimeout", "5000");
|
||||
javaMailSender.setJavaMailProperties(props);
|
||||
JavaMailSenderImpl javaMailSender = getMailSender();
|
||||
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
|
||||
String testName = "";
|
||||
if (type.equals("api")) {
|
||||
|
@ -73,38 +48,19 @@ public class MailService {
|
|||
testName = performanceResult.getName();
|
||||
status = performanceResult.getStatus();
|
||||
}
|
||||
String html1 = "<!DOCTYPE html>\n" +
|
||||
"<html lang=\"en\">\n" +
|
||||
"<head>\n" +
|
||||
" <meta charset=\"UTF-8\">\n" +
|
||||
" <title>MeterSphere</title>\n" +
|
||||
"</head>\n" +
|
||||
"<body style=\"text-align: left\">\n" +
|
||||
" <div>\n" +
|
||||
" <h3>" + type + Translator.get("timing_task_result_notification") + "</h3>\n" +
|
||||
" <p> 尊敬的用户:您好,<p><br/>" +
|
||||
"<p>您所执行的" + testName + "运行失败</p>\n" +
|
||||
" </div>\n" +
|
||||
"</body>\n" +
|
||||
"</html>";
|
||||
String html2 = "<!DOCTYPE html>\n" +
|
||||
"<html lang=\"en\">\n" +
|
||||
"<head>\n" +
|
||||
" <meta charset=\"UTF-8\">\n" +
|
||||
" <title>MeterSphere</title>\n" +
|
||||
"</head>\n" +
|
||||
"<body style=\"text-align: left\">\n" +
|
||||
" <div>\n" +
|
||||
" <h3>" + type + Translator.get("timing_task_result_notification") + "</h3>\n" +
|
||||
" <p> 尊敬的用户:您好," + testName + "运行成功</p>\n" +
|
||||
" </div>\n" +
|
||||
"</body>\n" +
|
||||
"</html>";
|
||||
|
||||
Map<String, String> context = new HashMap<>();
|
||||
context.put("title", type + Translator.get("timing_task_result_notification"));
|
||||
context.put("testName", testName);
|
||||
|
||||
try {
|
||||
String failTemplate = IOUtils.toString(this.getClass().getResource("/mail/fail.html"), StandardCharsets.UTF_8);
|
||||
String successTemplate = IOUtils.toString(this.getClass().getResource("/mail/success.html"), StandardCharsets.UTF_8);
|
||||
|
||||
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
|
||||
helper.setFrom(javaMailSender.getUsername());
|
||||
helper.setSubject(Translator.get("timing_task_result_notification"));
|
||||
String users[] = {};
|
||||
String[] users;
|
||||
List<String> successEmailList = new ArrayList<>();
|
||||
List<String> failEmailList = new ArrayList<>();
|
||||
if (notice.size() > 0) {
|
||||
|
@ -115,23 +71,20 @@ public class MailService {
|
|||
if (n.getEnable().equals("true") && n.getEvent().equals("执行失败")) {
|
||||
failEmailList = userService.queryEmail(n.getNames());
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
LogUtil.error("Recipient information is empty");
|
||||
}
|
||||
|
||||
if (status.equals("Success")) {
|
||||
users = successEmailList.toArray(new String[successEmailList.size()]);
|
||||
helper.setText(html2, true);
|
||||
users = successEmailList.toArray(new String[0]);
|
||||
helper.setText(getContent(successTemplate, context), true);
|
||||
} else {
|
||||
users = failEmailList.toArray(new String[failEmailList.size()]);
|
||||
helper.setText(html1, true);
|
||||
|
||||
users = failEmailList.toArray(new String[0]);
|
||||
helper.setText(getContent(failTemplate, context), true);
|
||||
}
|
||||
helper.setTo(users);
|
||||
|
||||
} catch (MessagingException e) {
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e);
|
||||
}
|
||||
try {
|
||||
|
@ -141,6 +94,17 @@ public class MailService {
|
|||
}
|
||||
}
|
||||
|
||||
private String getContent(String template, Map<String, String> context) {
|
||||
if (MapUtils.isNotEmpty(context)) {
|
||||
context.forEach((k, v) -> {
|
||||
if (v != null && !v.isEmpty()) {
|
||||
StringUtils.replaceAll(template, "${" + k + "}", v);
|
||||
}
|
||||
});
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
|
||||
public void sendHtml(List<String> userIds, String type, SaveTestCaseReviewRequest reviewRequest, SaveCommentRequest request, TestCaseWithBLOBs testCaseWithBLOBs) {
|
||||
Long startTime = reviewRequest.getCreateTime();
|
||||
|
@ -156,6 +120,61 @@ public class MailService {
|
|||
if (!eTime.equals("null")) {
|
||||
end = sdf.format(new Date(Long.parseLong(eTime)));
|
||||
}
|
||||
JavaMailSenderImpl javaMailSender = getMailSender();
|
||||
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
|
||||
|
||||
Map<String, String> context = new HashMap<>();
|
||||
context.put("creator", reviewRequest.getCreator());
|
||||
context.put("maintainer", testCaseWithBLOBs.getMaintainer());
|
||||
context.put("testCaseName", testCaseWithBLOBs.getName());
|
||||
context.put("reviewName", reviewRequest.getName());
|
||||
context.put("description", request.getDescription());
|
||||
context.put("start", start);
|
||||
context.put("end", end);
|
||||
|
||||
try {
|
||||
String reviewerTemplate = IOUtils.toString(this.getClass().getResource("/mail/reviewer.html"), StandardCharsets.UTF_8);
|
||||
String commentTemplate = IOUtils.toString(this.getClass().getResource("/mail/comment.html"), StandardCharsets.UTF_8);
|
||||
String endTemplate = IOUtils.toString(this.getClass().getResource("/mail/end.html"), StandardCharsets.UTF_8);
|
||||
|
||||
|
||||
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
|
||||
helper.setFrom(javaMailSender.getUsername());
|
||||
helper.setSubject(Translator.get("test_review_task_notice"));
|
||||
String[] users;
|
||||
List<String> emails = new ArrayList<>();
|
||||
try {
|
||||
emails = userService.queryEmailByIds(userIds);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error("Recipient information is empty");
|
||||
}
|
||||
users = emails.toArray(new String[0]);
|
||||
switch (type) {
|
||||
case "reviewer":
|
||||
helper.setText(getContent(reviewerTemplate, context), true);
|
||||
break;
|
||||
case "comment":
|
||||
helper.setText(getContent(commentTemplate, context), true);
|
||||
break;
|
||||
case "end":
|
||||
helper.setText(getContent(endTemplate, context), true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
helper.setTo(users);
|
||||
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e);
|
||||
}
|
||||
try {
|
||||
javaMailSender.send(mimeMessage);
|
||||
} catch (MailException e) {
|
||||
LogUtil.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private JavaMailSenderImpl getMailSender() {
|
||||
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
|
||||
List<SystemParameter> paramList = systemParameterService.getParamList(ParamConstants.Classify.MAIL.getValue());
|
||||
javaMailSender.setDefaultEncoding("UTF-8");
|
||||
|
@ -181,72 +200,7 @@ public class MailService {
|
|||
props.put("mail.smtp.timeout", "30000");
|
||||
props.put("mail.smtp.connectiontimeout", "5000");
|
||||
javaMailSender.setJavaMailProperties(props);
|
||||
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
|
||||
String html1 = "<!DOCTYPE html>\n" +
|
||||
"<html lang=\"en\">\n" +
|
||||
"<head>\n" +
|
||||
" <meta charset=\"UTF-8\">\n" +
|
||||
" <title>MeterSphere</title>\n" +
|
||||
"</head>\n" +
|
||||
"<body style=\"text-align: left\">\n" +
|
||||
" <div>\n" +
|
||||
" <p>" + reviewRequest.getCreator() + "发起的:" + "</p><br/>" + reviewRequest.getName() + "</p><br/>" + "计划开始时间是" + start + ",计划结束时间为" + end + "请跟进" + "</p>\n" +
|
||||
" </div>\n" +
|
||||
"</body>\n" +
|
||||
"</html>";
|
||||
String html2 = "<!DOCTYPE html>\n" +
|
||||
"<html lang=\"en\">\n" +
|
||||
"<head>\n" +
|
||||
" <meta charset=\"UTF-8\">\n" +
|
||||
" <title>MeterSphere</title>\n" +
|
||||
"</head>\n" +
|
||||
"<body style=\"text-align: left\">\n" +
|
||||
" <div>\n" +
|
||||
" <p>" + testCaseWithBLOBs.getMaintainer() + "发起的" + "</p><br/>" + testCaseWithBLOBs.getName() + "</p><br/>" + "添加评论:" + request.getDescription() + "</p>\n" +
|
||||
" </div>\n" +
|
||||
"</body>\n" +
|
||||
"</html>";
|
||||
String html3 = "<!DOCTYPE html>\n" +
|
||||
"<html lang=\"en\">\n" +
|
||||
"<head>\n" +
|
||||
" <meta charset=\"UTF-8\">\n" +
|
||||
" <title>MeterSphere</title>\n" +
|
||||
"</head>\n" +
|
||||
"<body style=\"text-align: left\">\n" +
|
||||
" <div>\n" +
|
||||
" <p>" + reviewRequest.getCreator() + "发起的:" + "</p><br/>" + reviewRequest.getName() + "</p><br/>" + "计划开始时间是" + start + ",计划结束时间为" + end + "已完成" + "</p>\n" +
|
||||
" </div>\n" +
|
||||
"</body>\n" +
|
||||
"</html>";
|
||||
try {
|
||||
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
|
||||
helper.setFrom(javaMailSender.getUsername());
|
||||
helper.setSubject(Translator.get("test_review_task_notice"));
|
||||
String users[] = {};
|
||||
List<String> emails = new ArrayList<>();
|
||||
try {
|
||||
emails = userService.queryEmailByIds(userIds);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error("Recipient information is empty");
|
||||
}
|
||||
users = emails.toArray(new String[emails.size()]);
|
||||
if (type.equals("reviewer")) {
|
||||
helper.setText(html1, true);
|
||||
} else if (type.equals("comment")) {
|
||||
helper.setText(html2, true);
|
||||
} else if (type.equals("end")) {
|
||||
helper.setText(html3, true);
|
||||
}
|
||||
helper.setTo(users);
|
||||
|
||||
} catch (MessagingException e) {
|
||||
LogUtil.error(e);
|
||||
}
|
||||
try {
|
||||
javaMailSender.send(mimeMessage);
|
||||
} catch (MailException e) {
|
||||
LogUtil.error(e);
|
||||
}
|
||||
return javaMailSender;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import io.metersphere.commons.utils.PageUtils;
|
|||
import io.metersphere.commons.utils.Pager;
|
||||
import io.metersphere.track.dto.TestReviewCaseDTO;
|
||||
import io.metersphere.track.request.testplancase.TestReviewCaseBatchRequest;
|
||||
import io.metersphere.track.request.testreview.DeleteRelevanceRequest;
|
||||
import io.metersphere.track.request.testreview.QueryCaseReviewRequest;
|
||||
import io.metersphere.track.service.TestReviewTestCaseService;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
|
@ -30,10 +31,10 @@ public class TestReviewTestCaseController {
|
|||
return PageUtils.setPageInfo(page, testReviewTestCaseService.list(request));
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
@PostMapping("/delete")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
public int deleteTestCase(@PathVariable String id) {
|
||||
return testReviewTestCaseService.deleteTestCase(id);
|
||||
public int deleteTestCase(@RequestBody DeleteRelevanceRequest request) {
|
||||
return testReviewTestCaseService.deleteTestCase(request);
|
||||
}
|
||||
|
||||
@PostMapping("/batch/delete")
|
||||
|
|
|
@ -9,5 +9,6 @@ import java.util.List;
|
|||
@Getter
|
||||
@Setter
|
||||
public class TestReviewCaseBatchRequest extends TestCaseReviewTestCase {
|
||||
private String reviewId;
|
||||
private List<String> ids;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package io.metersphere.track.request.testreview;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class DeleteRelevanceRequest {
|
||||
private String id;
|
||||
private String reviewId;
|
||||
}
|
|
@ -275,6 +275,13 @@ public class TestCaseReviewService {
|
|||
}
|
||||
|
||||
public void testReviewRelevance(ReviewRelevanceRequest request) {
|
||||
String reviewId = request.getReviewId();
|
||||
List<String> userIds = getTestCaseReviewerIds(reviewId);
|
||||
String currentId = SessionUtils.getUser().getId();
|
||||
if (!userIds.contains(currentId)) {
|
||||
MSException.throwException("非用例评审人员,不能关联用例!");
|
||||
}
|
||||
|
||||
List<String> testCaseIds = request.getTestCaseIds();
|
||||
|
||||
if (testCaseIds.isEmpty()) {
|
||||
|
@ -307,6 +314,13 @@ public class TestCaseReviewService {
|
|||
testCaseReviewMapper.updateByPrimaryKey(testCaseReview);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getTestCaseReviewerIds(String reviewId) {
|
||||
TestCaseReviewUsersExample testCaseReviewUsersExample = new TestCaseReviewUsersExample();
|
||||
testCaseReviewUsersExample.createCriteria().andReviewIdEqualTo(reviewId);
|
||||
List<TestCaseReviewUsers> testCaseReviewUsers = testCaseReviewUsersMapper.selectByExample(testCaseReviewUsersExample);
|
||||
return testCaseReviewUsers.stream().map(TestCaseReviewUsers::getUserId).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public TestCaseReview getTestReview(String reviewId) {
|
||||
return Optional.ofNullable(testCaseReviewMapper.selectByPrimaryKey(reviewId)).orElse(new TestCaseReview());
|
||||
|
|
|
@ -12,6 +12,7 @@ import io.metersphere.controller.request.member.QueryMemberRequest;
|
|||
import io.metersphere.service.UserService;
|
||||
import io.metersphere.track.dto.TestReviewCaseDTO;
|
||||
import io.metersphere.track.request.testplancase.TestReviewCaseBatchRequest;
|
||||
import io.metersphere.track.request.testreview.DeleteRelevanceRequest;
|
||||
import io.metersphere.track.request.testreview.QueryCaseReviewRequest;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
@ -35,6 +36,8 @@ public class TestReviewTestCaseService {
|
|||
TestCaseReviewUsersMapper testCaseReviewUsersMapper;
|
||||
@Resource
|
||||
TestCaseReviewMapper testCaseReviewMapper;
|
||||
@Resource
|
||||
TestCaseReviewService testCaseReviewService;
|
||||
|
||||
public List<TestReviewCaseDTO> list(QueryCaseReviewRequest request) {
|
||||
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
|
||||
|
@ -71,11 +74,21 @@ public class TestReviewTestCaseService {
|
|||
return name;
|
||||
}
|
||||
|
||||
public int deleteTestCase(String id) {
|
||||
return testCaseReviewTestCaseMapper.deleteByPrimaryKey(id);
|
||||
public int deleteTestCase(DeleteRelevanceRequest request) {
|
||||
checkReviewer(request.getReviewId());
|
||||
return testCaseReviewTestCaseMapper.deleteByPrimaryKey(request.getId());
|
||||
}
|
||||
|
||||
private void checkReviewer(String reviewId) {
|
||||
List<String> userIds = testCaseReviewService.getTestCaseReviewerIds(reviewId);
|
||||
String currentId = SessionUtils.getUser().getId();
|
||||
if (!userIds.contains(currentId)) {
|
||||
MSException.throwException("非用例评审人员,不能解除用例关联!");
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteTestCaseBath(TestReviewCaseBatchRequest request) {
|
||||
checkReviewer(request.getReviewId());
|
||||
TestCaseReviewTestCaseExample example = new TestCaseReviewTestCaseExample();
|
||||
example.createCriteria().andIdIn(request.getIds());
|
||||
testCaseReviewTestCaseMapper.deleteByExample(example);
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>MeterSphere</title>
|
||||
</head>
|
||||
<body style="text-align: left">
|
||||
<div>
|
||||
<p>${maintainer} 发起的</p><br/> ${testCaseName} </p><br/>添加评论:${description} </p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>MeterSphere</title>
|
||||
</head>
|
||||
<body style="text-align: left">
|
||||
<div>
|
||||
<p>${creator} 发起的:</p><br/>${reviewName} </p><br/>计划开始时间是 ${start}, 计划结束时间为 ${end} 已完成</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>MeterSphere</title>
|
||||
</head>
|
||||
<body style="text-align: left">
|
||||
<div>
|
||||
<h3>${title}</h3>
|
||||
<p>尊敬的用户:您好,
|
||||
<p><br/>
|
||||
<p>您所执行的 ${testName} 运行失败</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>MeterSphere</title>
|
||||
</head>
|
||||
<body style="text-align: left">
|
||||
<div>
|
||||
<p>${creator} 发起的:</p><br/>${reviewName} </p><br/>计划开始时间是 ${start},计划结束时间为 ${end} 请跟进</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>MeterSphere</title>
|
||||
</head>
|
||||
<body style="text-align: left">
|
||||
<div>
|
||||
<h3>${title}</h3>
|
||||
<p>尊敬的用户:您好,
|
||||
<p><br/>
|
||||
<p>您所执行的 ${testName} 运行成功</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -322,7 +322,7 @@ export default {
|
|||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
let ids = Array.from(this.selectRows).map(row => row.id);
|
||||
this.$post('/test/review/case/batch/delete', {ids: ids}, () => {
|
||||
this.$post('/test/review/case/batch/delete', {ids: ids, reviewId: this.reviewId}, () => {
|
||||
this.selectRows.clear();
|
||||
this.$emit("refresh");
|
||||
this.$success(this.$t('test_track.cancel_relevance_success'));
|
||||
|
@ -333,7 +333,7 @@ export default {
|
|||
},
|
||||
_handleDelete(testCase) {
|
||||
let testCaseId = testCase.id;
|
||||
this.$post('/test/review/case/delete/' + testCaseId, {}, () => {
|
||||
this.$post('/test/review/case/delete', {id: testCaseId, reviewId: testCase.reviewId}, () => {
|
||||
this.$emit("refresh");
|
||||
this.$success(this.$t('test_track.cancel_relevance_success'));
|
||||
});
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 06d935cd1d22ab36f09763745c2aff8ad3fb08c1
|
||||
Subproject commit cc38137a69a0f20fadece9c0f9f50a9468c4ace9
|
Loading…
Reference in New Issue