Skip to content

Commit

Permalink
warnings: кнопка закрытия
Browse files Browse the repository at this point in the history
  • Loading branch information
maxcom committed Nov 2, 2024
1 parent a2f2f92 commit 186cbdc
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 16 deletions.
21 changes: 21 additions & 0 deletions sql/updates/2024-10-26-warnings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,25 @@
<column name="postdate"/>
</createIndex>
</changeSet>

<changeSet id="2024110201" author="Maxim Valyanskiy">
<addColumn tableName="message_warnings">
<column name="closed_by" type="int">
<constraints nullable="true" references="users(id)" foreignKeyName="message_warnings_closed_by_fkey"/>
</column>
<column name="closed_when" type="timestamptz"/>
</addColumn>
</changeSet>

<changeSet id="2024110202" author="Maxim Valyanskiy">
<createIndex tableName="message_warnings" indexName="message_warnings_closed_by_idx">
<column name="closed_by"/>
</createIndex>
</changeSet>

<changeSet id="2024110203" author="Maxim Valyanskiy">
<sql>
grant update on message_warnings to linuxweb
</sql>
</changeSet>
</databaseChangeLog>
11 changes: 8 additions & 3 deletions src/main/scala/ru/org/linux/warning/Warning.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
26 changes: 22 additions & 4 deletions src/main/scala/ru/org/linux/warning/WarningController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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() {
Expand Down
30 changes: 25 additions & 5 deletions src/main/scala/ru/org/linux/warning/WarningDao.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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] = {
Expand All @@ -54,21 +56,39 @@ 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)
}

def lastWarningsCount(userId: Int): Int =
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)
}
}
8 changes: 7 additions & 1 deletion src/main/scala/ru/org/linux/warning/WarningService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
20 changes: 17 additions & 3 deletions src/main/webapp/WEB-INF/tags/warnings.tag
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,23 @@
<c:if test="${not empty warnings}">
<div class="infoblock">
<c:forEach var="warning" items="${warnings}">
<div>
⚠️${' '} <lor:date date="${warning.postdate}"/> ${' '} <lor:user user="${warning.author}" link="true"/>:
<c:out value="${warning.message}" escapeXml="true"/>
<div style="margin-bottom: 0.5em">
⚠️${' '}
<c:if test="${warning.closed}"><s></c:if>
<lor:date date="${warning.postdate}"/> ${' '} <lor:user user="${warning.author}" link="true"/>:
<c:out value="${warning.message}" escapeXml="true"/>
<c:if test="${warning.closed}"></s>
(закрыт <lor:user user="${warning.closedBy}" link="true"/>)
</c:if>

<c:if test="${not warning.closed}">
&nbsp;
<form action="clear-warning" method="POST" style="display: inline-block">
<lor:csrf/>
<input type="hidden" name="id" value="${warning.id}">
<button type="submit" class="btn btn-small btn-default">закрыть</button>
</form>
</c:if>
</div>
</c:forEach>
</div>
Expand Down

0 comments on commit 186cbdc

Please sign in to comment.