diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseCommentMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseCommentMapper.java new file mode 100644 index 0000000000..60f44b7202 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseCommentMapper.java @@ -0,0 +1,17 @@ +package io.metersphere.base.mapper.ext; + +import io.metersphere.track.dto.TestCaseCommentDTO; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +public interface ExtTestCaseCommentMapper { + + /** + * 获取用例的评论 + * @param caseId + * @return + */ + List getCaseComments(@Param("caseId") String caseId); + +} diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseCommentMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseCommentMapper.xml new file mode 100644 index 0000000000..9a92b100cd --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseCommentMapper.xml @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/ldap/service/CustomSSLSocketFactory.java b/backend/src/main/java/io/metersphere/ldap/service/CustomSSLSocketFactory.java new file mode 100644 index 0000000000..8a25c8c47a --- /dev/null +++ b/backend/src/main/java/io/metersphere/ldap/service/CustomSSLSocketFactory.java @@ -0,0 +1,84 @@ +package io.metersphere.ldap.service; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +public class CustomSSLSocketFactory extends SSLSocketFactory { + private SSLSocketFactory socketFactory; + + public CustomSSLSocketFactory() { + try { + SSLContext ctx = SSLContext.getInstance("TLS"); + ctx.init(null, new TrustManager[]{new DummyTrustmanager()}, new SecureRandom()); + socketFactory = ctx.getSocketFactory(); + } catch (Exception ex) { + ex.printStackTrace(System.err); + } + } + + public static SocketFactory getDefault() { + return new CustomSSLSocketFactory(); + } + + @Override + public String[] getDefaultCipherSuites() { + return socketFactory.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return socketFactory.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket(Socket socket, String string, int num, boolean bool) throws IOException { + return socketFactory.createSocket(socket, string, num, bool); + } + + @Override + public Socket createSocket(String string, int num) throws IOException, UnknownHostException { + return socketFactory.createSocket(string, num); + } + + @Override + public Socket createSocket(String string, int num, InetAddress netAdd, int i) throws IOException, UnknownHostException { + return socketFactory.createSocket(string, num, netAdd, i); + } + + @Override + public Socket createSocket(InetAddress netAdd, int num) throws IOException { + return socketFactory.createSocket(netAdd, num); + } + + @Override + public Socket createSocket(InetAddress netAdd1, int num, InetAddress netAdd2, int i) throws IOException { + return socketFactory.createSocket(netAdd1, num, netAdd2, i); + } + + + /** + * 证书 + */ + public static class DummyTrustmanager implements X509TrustManager { + public void checkClientTrusted(X509Certificate[] cert, String string) throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] cert, String string) throws CertificateException { + } + + public X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[0]; + } + + } +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/ldap/service/LdapService.java b/backend/src/main/java/io/metersphere/ldap/service/LdapService.java index 03436c179a..44aa6bc84a 100644 --- a/backend/src/main/java/io/metersphere/ldap/service/LdapService.java +++ b/backend/src/main/java/io/metersphere/ldap/service/LdapService.java @@ -146,8 +146,17 @@ public class LdapService { preConnect(url, dn, password); String credentials = EncryptUtils.aesDecrypt(password).toString(); - - LdapContextSource sourceLdapCtx = new LdapContextSource(); + LdapContextSource sourceLdapCtx; + if (StringUtils.startsWith(url, "ldaps://")) { + sourceLdapCtx = new SSLLdapContextSource(); + // todo 这里加上strategy 会报错 +// DefaultTlsDirContextAuthenticationStrategy strategy = new DefaultTlsDirContextAuthenticationStrategy(); +// strategy.setShutdownTlsGracefully(true); +// strategy.setHostnameVerifier((hostname, session) -> true); +// sourceLdapCtx.setAuthenticationStrategy(strategy); + } else { + sourceLdapCtx = new LdapContextSource(); + } sourceLdapCtx.setUrl(url); sourceLdapCtx.setUserDn(dn); sourceLdapCtx.setPassword(credentials); diff --git a/backend/src/main/java/io/metersphere/ldap/service/SSLLdapContextSource.java b/backend/src/main/java/io/metersphere/ldap/service/SSLLdapContextSource.java new file mode 100644 index 0000000000..05f302d02b --- /dev/null +++ b/backend/src/main/java/io/metersphere/ldap/service/SSLLdapContextSource.java @@ -0,0 +1,16 @@ +package io.metersphere.ldap.service; + +import org.springframework.ldap.core.support.LdapContextSource; + +import javax.naming.Context; +import java.util.Hashtable; + +public class SSLLdapContextSource extends LdapContextSource { + public Hashtable getAnonymousEnv() { + Hashtable anonymousEnv = super.getAnonymousEnv(); + anonymousEnv.put("java.naming.security.protocol", "ssl"); + anonymousEnv.put("java.naming.ldap.factory.socket", CustomSSLSocketFactory.class.getName()); + anonymousEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + return anonymousEnv; + } +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/track/controller/TestCaseCommentController.java b/backend/src/main/java/io/metersphere/track/controller/TestCaseCommentController.java index 204c3fac59..5456ee976c 100644 --- a/backend/src/main/java/io/metersphere/track/controller/TestCaseCommentController.java +++ b/backend/src/main/java/io/metersphere/track/controller/TestCaseCommentController.java @@ -1,6 +1,6 @@ package io.metersphere.track.controller; -import io.metersphere.base.domain.TestCaseComment; +import io.metersphere.track.dto.TestCaseCommentDTO; import io.metersphere.track.request.testreview.SaveCommentRequest; import io.metersphere.track.service.TestCaseCommentService; import org.springframework.web.bind.annotation.*; @@ -13,7 +13,7 @@ import java.util.List; public class TestCaseCommentController { @Resource - TestCaseCommentService testCaseCommentService; + private TestCaseCommentService testCaseCommentService; @PostMapping("/save") public void saveComment(@RequestBody SaveCommentRequest request) { @@ -21,7 +21,17 @@ public class TestCaseCommentController { } @GetMapping("/list/{caseId}") - public List getComments(@PathVariable String caseId) { - return testCaseCommentService.getComments(caseId); + public List getCaseComments(@PathVariable String caseId) { + return testCaseCommentService.getCaseComments(caseId); + } + + @GetMapping("/delete/{commentId}") + public void deleteComment(@PathVariable String commentId) { + testCaseCommentService.delete(commentId); + } + + @PostMapping("/edit") + public void editComment(@RequestBody SaveCommentRequest request) { + testCaseCommentService.edit(request); } } diff --git a/backend/src/main/java/io/metersphere/track/controller/TestCaseReviewController.java b/backend/src/main/java/io/metersphere/track/controller/TestCaseReviewController.java index 6236c1f20b..0a89b86c11 100644 --- a/backend/src/main/java/io/metersphere/track/controller/TestCaseReviewController.java +++ b/backend/src/main/java/io/metersphere/track/controller/TestCaseReviewController.java @@ -106,7 +106,7 @@ public class TestCaseReviewController { } - @PostMapping("/get/{reviewId}") + @GetMapping("/get/{reviewId}") public TestCaseReview getTestReview(@PathVariable String reviewId) { checkOwnerService.checkTestReviewOwner(reviewId); return testCaseReviewService.getTestReview(reviewId); diff --git a/backend/src/main/java/io/metersphere/track/dto/TestCaseCommentDTO.java b/backend/src/main/java/io/metersphere/track/dto/TestCaseCommentDTO.java new file mode 100644 index 0000000000..3cd40001e9 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/dto/TestCaseCommentDTO.java @@ -0,0 +1,9 @@ +package io.metersphere.track.dto; + +import io.metersphere.base.domain.TestCaseComment; +import lombok.Data; + +@Data +public class TestCaseCommentDTO extends TestCaseComment { + private String authorName; +} diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseCommentService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseCommentService.java index b3aeaef768..f7d0f28af4 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseCommentService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseCommentService.java @@ -3,21 +3,23 @@ package io.metersphere.track.service; import io.metersphere.base.domain.TestCaseComment; import io.metersphere.base.domain.TestCaseCommentExample; import io.metersphere.base.domain.TestCaseWithBLOBs; -import io.metersphere.base.domain.User; import io.metersphere.base.mapper.TestCaseCommentMapper; import io.metersphere.base.mapper.TestCaseMapper; -import io.metersphere.base.mapper.TestCaseReviewMapper; -import io.metersphere.base.mapper.UserMapper; +import io.metersphere.base.mapper.ext.ExtTestCaseCommentMapper; import io.metersphere.commons.constants.NoticeConstants; +import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.SessionUtils; +import io.metersphere.i18n.Translator; import io.metersphere.notice.domain.MessageDetail; import io.metersphere.notice.domain.MessageSettingDetail; import io.metersphere.notice.service.DingTaskService; import io.metersphere.notice.service.MailService; import io.metersphere.notice.service.NoticeService; import io.metersphere.notice.service.WxChatTaskService; +import io.metersphere.track.dto.TestCaseCommentDTO; import io.metersphere.track.request.testreview.SaveCommentRequest; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -33,21 +35,19 @@ import java.util.UUID; public class TestCaseCommentService { @Resource - TestCaseCommentMapper testCaseCommentMapper; + private TestCaseCommentMapper testCaseCommentMapper; @Resource - private TestCaseReviewMapper testCaseReviewMapper; + private MailService mailService; @Resource - UserMapper userMapper; + private TestCaseMapper testCaseMapper; @Resource - MailService mailService; + private DingTaskService dingTaskService; @Resource - TestCaseMapper testCaseMapper; + private WxChatTaskService wxChatTaskService; @Resource - DingTaskService dingTaskService; + private NoticeService noticeService; @Resource - WxChatTaskService wxChatTaskService; - @Resource - NoticeService noticeService; + private ExtTestCaseCommentMapper extTestCaseCommentMapper; public void saveComment(SaveCommentRequest request) { @@ -86,21 +86,11 @@ public class TestCaseCommentService { } - public List getComments(String caseId) { - TestCaseCommentExample testCaseCommentExample = new TestCaseCommentExample(); - testCaseCommentExample.setOrderByClause("update_time desc"); - testCaseCommentExample.createCriteria().andCaseIdEqualTo(caseId); - List testCaseComments = testCaseCommentMapper.selectByExampleWithBLOBs(testCaseCommentExample); - testCaseComments.forEach(testCaseComment -> { - String authorId = testCaseComment.getAuthor(); - User user = userMapper.selectByPrimaryKey(authorId); - String author = user == null ? authorId : user.getName(); - testCaseComment.setAuthor(author); - }); - return testCaseComments; + public List getCaseComments(String caseId) { + return extTestCaseCommentMapper.getCaseComments(caseId); } - public void deleteComment(String caseId) { + public void deleteCaseComment(String caseId) { TestCaseCommentExample testCaseCommentExample = new TestCaseCommentExample(); testCaseCommentExample.createCriteria().andCaseIdEqualTo(caseId); testCaseCommentMapper.deleteByExample(testCaseCommentExample); @@ -118,4 +108,22 @@ public class TestCaseCommentService { context = "测试评审任务通知:" + testCaseComment.getAuthor() + "在" + start + "为" + "'" + testCaseWithBLOBs.getName() + "'" + "添加评论:" + testCaseComment.getDescription(); return context; } + + public void delete(String commentId) { + checkCommentOwner(commentId); + testCaseCommentMapper.deleteByPrimaryKey(commentId); + } + + public void edit(SaveCommentRequest request) { + checkCommentOwner(request.getId()); + testCaseCommentMapper.updateByPrimaryKeySelective(request); + } + + private void checkCommentOwner(String commentId) { + TestCaseComment comment = testCaseCommentMapper.selectByPrimaryKey(commentId); + if (!StringUtils.equals(comment.getAuthor(), SessionUtils.getUser().getId())) { + MSException.throwException(Translator.get("check_owner_comment")); + } + } + } diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java index 22903d2566..657cf282a0 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java @@ -170,7 +170,7 @@ public class TestCaseService { example.createCriteria().andCaseIdEqualTo(testCaseId); testPlanTestCaseMapper.deleteByExample(example); testCaseIssueService.delTestCaseIssues(testCaseId); - testCaseCommentService.deleteComment(testCaseId); + testCaseCommentService.deleteCaseComment(testCaseId); return testCaseMapper.deleteByPrimaryKey(testCaseId); } diff --git a/backend/src/main/resources/db/migration/V37__add_plan_index.sql b/backend/src/main/resources/db/migration/V37__add_plan_index.sql new file mode 100644 index 0000000000..762780f3e6 --- /dev/null +++ b/backend/src/main/resources/db/migration/V37__add_plan_index.sql @@ -0,0 +1,2 @@ +ALTER TABLE `test_plan_test_case` ADD INDEX index_name ( `case_id` ); +ALTER TABLE `test_case_review_test_case` ADD INDEX index_name ( `case_id` ); \ No newline at end of file diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties index bd58c4e206..d3ff598c78 100644 --- a/backend/src/main/resources/i18n/messages_en_US.properties +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -164,6 +164,7 @@ check_owner_test=The current user does not have permission to operate this test check_owner_case=The current user does not have permission to operate this use case check_owner_plan=The current user does not have permission to operate this plan check_owner_review=The current user does not have permission to operate this review +check_owner_comment=The current user does not have permission to manipulate this comment upload_content_is_null=Imported content is empty test_plan_notification=Test plan notification task_defect_notification=Task defect notification diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index 91d5ffa4b6..9b172e75dd 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -164,6 +164,7 @@ check_owner_test=当前用户没有操作此测试的权限 check_owner_case=当前用户没有操作此用例的权限 check_owner_plan=当前用户没有操作此计划的权限 check_owner_review=当前用户没有操作此评审的权限 +check_owner_comment=当前用户没有操作此评论的权限 upload_content_is_null=导入内容为空 test_plan_notification=测试计划通知 task_defect_notification=缺陷任务通知 diff --git a/backend/src/main/resources/i18n/messages_zh_TW.properties b/backend/src/main/resources/i18n/messages_zh_TW.properties index a34799eac5..b46f36cf88 100644 --- a/backend/src/main/resources/i18n/messages_zh_TW.properties +++ b/backend/src/main/resources/i18n/messages_zh_TW.properties @@ -165,6 +165,7 @@ check_owner_test=當前用戶沒有操作此測試的權限 check_owner_case=當前用戶沒有操作此用例的權限 check_owner_plan=當前用戶沒有操作此計劃的權限 check_owner_review=當前用戶沒有操作此評審的權限 +check_owner_comment=當前用戶沒有操作此評論的權限 upload_content_is_null=導入內容為空 test_plan_notification=測試計畫通知 task_defect_notification=缺陷任務通知 diff --git a/frontend/src/business/components/settings/organization/TaskNotification.vue b/frontend/src/business/components/settings/organization/TaskNotification.vue index 058d4c9b51..7b6838795e 100644 --- a/frontend/src/business/components/settings/organization/TaskNotification.vue +++ b/frontend/src/business/components/settings/organization/TaskNotification.vue @@ -1,488 +1,48 @@ + + diff --git a/frontend/src/business/components/settings/organization/components/JenkinsNotification.vue b/frontend/src/business/components/settings/organization/components/JenkinsNotification.vue new file mode 100644 index 0000000000..c1bb5da081 --- /dev/null +++ b/frontend/src/business/components/settings/organization/components/JenkinsNotification.vue @@ -0,0 +1,221 @@ + + + + + diff --git a/frontend/src/business/components/settings/organization/components/TestPlanTaskNotification.vue b/frontend/src/business/components/settings/organization/components/TestPlanTaskNotification.vue new file mode 100644 index 0000000000..630f769e87 --- /dev/null +++ b/frontend/src/business/components/settings/organization/components/TestPlanTaskNotification.vue @@ -0,0 +1,242 @@ + + + + + diff --git a/frontend/src/business/components/settings/organization/components/TestReviewNotification.vue b/frontend/src/business/components/settings/organization/components/TestReviewNotification.vue new file mode 100644 index 0000000000..91aaadb4a6 --- /dev/null +++ b/frontend/src/business/components/settings/organization/components/TestReviewNotification.vue @@ -0,0 +1,248 @@ + + + + + diff --git a/frontend/src/business/components/track/home/TrackHome.vue b/frontend/src/business/components/track/home/TrackHome.vue index dd90b15cd5..7416f3a6ef 100644 --- a/frontend/src/business/components/track/home/TrackHome.vue +++ b/frontend/src/business/components/track/home/TrackHome.vue @@ -3,18 +3,17 @@ - + + + + + + -
- - - - - @@ -52,4 +51,7 @@ export default { cursor: pointer; } +.el-row { + padding-bottom: 20px; +} diff --git a/frontend/src/business/components/track/plan/view/comonents/TestPlanTestCaseEdit.vue b/frontend/src/business/components/track/plan/view/comonents/TestPlanTestCaseEdit.vue index b3840820da..61937b1282 100644 --- a/frontend/src/business/components/track/plan/view/comonents/TestPlanTestCaseEdit.vue +++ b/frontend/src/business/components/track/plan/view/comonents/TestPlanTestCaseEdit.vue @@ -101,7 +101,7 @@ {{ $t('test_track.case.prerequisite') }}: - {{ testCase.prerequisite }} +

{{ testCase.prerequisite }}

@@ -684,4 +684,9 @@ export default { .el-switch >>> .el-switch__label.is-active { color: #409EFF; } + +p { + white-space: pre-line; + line-height: 20px; +} diff --git a/frontend/src/business/components/track/review/commom/ReviewComment.vue b/frontend/src/business/components/track/review/commom/ReviewComment.vue index 0733a0235e..62e0b40ad2 100644 --- a/frontend/src/business/components/track/review/commom/ReviewComment.vue +++ b/frontend/src/business/components/track/review/commom/ReviewComment.vue @@ -1,7 +1,10 @@ @@ -72,4 +130,10 @@ export default { pre { margin: 0 0; } + +.comment-delete { + float: right; + margin-right: 5px; + cursor: pointer; +} diff --git a/frontend/src/business/components/track/review/view/components/TestReviewTestCaseList.vue b/frontend/src/business/components/track/review/view/components/TestReviewTestCaseList.vue index 6d0da9dfb0..122ab4ef4f 100644 --- a/frontend/src/business/components/track/review/view/components/TestReviewTestCaseList.vue +++ b/frontend/src/business/components/track/review/view/components/TestReviewTestCaseList.vue @@ -365,7 +365,7 @@ export default { }, getTestReviewById() { if (this.reviewId) { - this.$post('/test/case/review/get/' + this.reviewId, {}, response => { + this.$get('/test/case/review/get/' + this.reviewId, response => { this.testReview = response.data; this.refreshTestReviewRecent(); }); diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index f2518841ad..afdb78b19f 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -848,6 +848,8 @@ export default { relevance_case: "Relevance Case", last_page: "It's the end!", execute_result: "Result", + cannot_edit: "Cannot edit this comment!", + cannot_delete: "Cannot delete this comment!", }, module: { search: "Search module", diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index 4f369a478b..cf63c0527e 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -841,6 +841,8 @@ export default { send: "发送", description_is_null: "评论内容不能为空!", send_success: "评论成功!", + cannot_edit: "无法编辑此评论!", + cannot_delete: "无法删除此评论!", }, review_view: { review: "评审", diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index 23fec07053..49ccd7ec0d 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -846,6 +846,8 @@ export default { send: "發送", description_is_null: "評論內容不能為空!", send_success: "評論成功!", + cannot_edit: "無法編輯此評論!", + cannot_delete: "無法刪除此評論!", }, review_view: { review: "評審",