diff --git a/sql/updates/2024-10-26-warnings.xml b/sql/updates/2024-10-26-warnings.xml index c4fd9a22b..baf2a781a 100644 --- a/sql/updates/2024-10-26-warnings.xml +++ b/sql/updates/2024-10-26-warnings.xml @@ -78,4 +78,25 @@ + + + + + + + + + + + + + + + + + + + grant update on message_warnings to linuxweb + + diff --git a/src/main/scala/ru/org/linux/warning/Warning.scala b/src/main/scala/ru/org/linux/warning/Warning.scala index de0db3dcf..e28c656dc 100644 --- a/src/main/scala/ru/org/linux/warning/Warning.scala +++ b/src/main/scala/ru/org/linux/warning/Warning.scala @@ -19,7 +19,8 @@ import ru.org.linux.user.User import java.time.Instant import java.util.Date -import scala.beans.BeanProperty +import javax.annotation.Nullable +import scala.beans.{BeanProperty, BooleanBeanProperty} sealed trait WarningType { def id: String @@ -51,6 +52,10 @@ object SpellingWarning extends WarningType { } case class Warning(id: Int, topicId: Int, commentId: Option[Int], postdate: Instant, authorId: Int, message: String, - warningType: WarningType) + warningType: WarningType, closedBy: Option[Int], closedWhen: Option[Instant]) -case class PreparedWarning(@BeanProperty postdate: Date, @BeanProperty author: User, @BeanProperty message: String) +case class PreparedWarning(@BeanProperty postdate: Date, @BeanProperty author: User, @BeanProperty message: String, + @BeanProperty id: Int, @BeanProperty @Nullable closedBy: User) { + // for jsp + def isClosed = closedBy != null +} diff --git a/src/main/scala/ru/org/linux/warning/WarningController.scala b/src/main/scala/ru/org/linux/warning/WarningController.scala index 06cb8899f..364fead6d 100644 --- a/src/main/scala/ru/org/linux/warning/WarningController.scala +++ b/src/main/scala/ru/org/linux/warning/WarningController.scala @@ -18,8 +18,10 @@ package ru.org.linux.warning import org.springframework.stereotype.Controller import org.springframework.validation.Errors import org.springframework.web.bind.WebDataBinder -import org.springframework.web.bind.annotation.{InitBinder, ModelAttribute, RequestMapping, RequestMethod} +import org.springframework.web.bind.annotation.{InitBinder, ModelAttribute, RequestMapping, RequestMethod, RequestParam, RequestPart} import org.springframework.web.servlet.ModelAndView +import org.springframework.web.servlet.view.RedirectView +import ru.org.linux.auth.AuthUtil.{AuthorizedOnly, CorrectorOrModerator} import ru.org.linux.auth.{AccessViolationException, AuthUtil, CurrentUser} import ru.org.linux.comment.{Comment, CommentPrepareService, CommentReadService} import ru.org.linux.group.{Group, GroupDao} @@ -42,7 +44,7 @@ class WarningController(warningService: WarningService, topicDao: TopicDao, comm topicPrepareService: TopicPrepareService, commentPrepareService: CommentPrepareService) { @RequestMapping(value = Array("/post-warning"), method = Array(RequestMethod.GET)) def showForm(@ModelAttribute(value = "request") request: PostWarningRequest, - errors: Errors): ModelAndView = AuthUtil.AuthorizedOnly { currentUser => + errors: Errors): ModelAndView = AuthorizedOnly { currentUser => val group = groupDao.getGroup(request.topic.groupId) checkRequest(group, request, errors, currentUser) @@ -82,8 +84,8 @@ class WarningController(warningService: WarningService, topicDao: TopicDao, comm } @RequestMapping(value = Array("/post-warning"), method = Array(RequestMethod.POST)) - def post(@ModelAttribute(value = "request") request: PostWarningRequest, - errors: Errors): ModelAndView = AuthUtil.AuthorizedOnly { currentUser => + def post(@ModelAttribute(value = "request") request: PostWarningRequest, + errors: Errors): ModelAndView = AuthorizedOnly { currentUser => val group = groupDao.getGroup(request.topic.groupId) checkRequest(group, request, errors, currentUser) @@ -162,6 +164,22 @@ class WarningController(warningService: WarningService, topicDao: TopicDao, comm } } + @RequestMapping(value = Array("/clear-warning"), method = Array(RequestMethod.POST)) + def clear(@RequestParam(value = "id") id: Int): ModelAndView = CorrectorOrModerator { currentUser => + val warning = warningService.get(id) + val topic = topicDao.getById(warning.topicId) + + warningService.clear(warning, currentUser) + + val builder = TopicLinkBuilder.baseLink(topic) + + val link = warning.commentId.map { commentId => + builder.comment(commentId) + }.getOrElse(builder).build() + + new ModelAndView(new RedirectView(link)) + } + @InitBinder def initBinder(binder: WebDataBinder): Unit = { binder.registerCustomEditor(classOf[Topic], new PropertyEditorSupport() { diff --git a/src/main/scala/ru/org/linux/warning/WarningDao.scala b/src/main/scala/ru/org/linux/warning/WarningDao.scala index 3af72e7ff..b1c9a4c42 100644 --- a/src/main/scala/ru/org/linux/warning/WarningDao.scala +++ b/src/main/scala/ru/org/linux/warning/WarningDao.scala @@ -44,7 +44,9 @@ class WarningDao(ds: DataSource) { commentId = Some(rs.getInt("comment")).filter(_ != 0), postdate = rs.getTimestamp("postdate").toInstant, message = rs.getString("message"), - warningType = WarningType.idToType(rs.getString("warning_type"))) + warningType = WarningType.idToType(rs.getString("warning_type")), + closedBy = Some(rs.getInt("closed_by")).filter(_ != 0), + closedWhen = Option(rs.getTimestamp("closed_when")).map(_.toInstant)) } def loadForTopic(topicId: Int, forModerator: Boolean): collection.Seq[Warning] = { @@ -54,16 +56,18 @@ class WarningDao(ds: DataSource) { "and warning_type IN ('tag', 'spelling') " } - namedJdbcTemplate.query("select id, topic, comment, postdate, author, message, warning_type from message_warnings " + - "where topic=:topic and comment is null and postdate>CURRENT_TIMESTAMP-'5 days'::interval " + filter + + namedJdbcTemplate.query("select id, topic, comment, postdate, author, message, warning_type, closed_by, closed_when " + + "from message_warnings " + + "where topic=:topic and comment is null " + filter + "order by postdate", Map("topic" -> topicId).asJava, mapper).asScala } def loadForComments(comments: Set[Int]): Map[Int, Seq[Warning]] = { val params = Map("list" -> comments.asJava) - namedJdbcTemplate.query("select id, topic, comment, postdate, author, message, warning_type from message_warnings " + - "where comment in (:list) and postdate>CURRENT_TIMESTAMP-'5 days'::interval " + + namedJdbcTemplate.query("select id, topic, comment, postdate, author, message, warning_type, closed_by, closed_when " + + "from message_warnings " + + "where comment in (:list) " + "order by postdate", params.asJava, mapper).asScala.toVector.groupBy(_.commentId.get) } @@ -71,4 +75,20 @@ class WarningDao(ds: DataSource) { namedJdbcTemplate.queryForObject("select count(*) from message_warnings where " + "postdate > CURRENT_TIMESTAMP-'1 hour'::interval and author = :author", Map("author" -> userId).asJava, classOf[Int]) + + def get(id: Int): Warning = + namedJdbcTemplate.queryForObject("select id, topic, comment, postdate, author, message, warning_type, closed_by, closed_when " + + "from message_warnings " + + "where id=:id " + + "order by postdate", Map("id" -> id).asJava, mapper) + + def clear(id: Int, byUserId: Int): Unit = { + val params = Map( + "byUserId" -> byUserId, + "id" -> id + ) + + namedJdbcTemplate.update("update message_warnings set closed_by = :byUserId, closed_when = CURRENT_TIMESTAMP " + + "where id=:id and closed_by is null", params.asJava) + } } diff --git a/src/main/scala/ru/org/linux/warning/WarningService.scala b/src/main/scala/ru/org/linux/warning/WarningService.scala index 6e4f2d661..18df4c64a 100644 --- a/src/main/scala/ru/org/linux/warning/WarningService.scala +++ b/src/main/scala/ru/org/linux/warning/WarningService.scala @@ -56,11 +56,17 @@ class WarningService(warningDao: WarningDao, eventService: UserEventService, use PreparedWarning( postdate = new Date(warning.postdate.toEpochMilli), author = userService.getUserCached(warning.authorId), - message = text) + message = text, + id = warning.id, + closedBy = warning.closedBy.map(userService.getUserCached).orNull) } def load(topic: Topic, forModerator: Boolean): Seq[Warning] = warningDao.loadForTopic(topic.id, forModerator).toVector def load(comments: Seq[Comment]): Map[Int, Seq[Warning]] = warningDao.loadForComments(comments.map(_.id).toSet) + + def get(id: Int): Warning = warningDao.get(id) + + def clear(warning: Warning, by: CurrentUser) = warningDao.clear(warning.id, by.user.getId) } \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/tags/warnings.tag b/src/main/webapp/WEB-INF/tags/warnings.tag index 07853b883..979543c6a 100644 --- a/src/main/webapp/WEB-INF/tags/warnings.tag +++ b/src/main/webapp/WEB-INF/tags/warnings.tag @@ -20,9 +20,23 @@
-
- ⚠️${' '} ${' '} : - +
+ ⚠️${' '} + + ${' '} : + + + (закрыт ) + + + +   +
+ + + + +