doortts doortts 2018-02-07
issue: Display label changing history at issue timeline
See: Yona Github issue #355
@a56074b207bb53f7a2fe77aa0593cc4eadee0f0d
app/assets/stylesheets/less/_common.less
--- app/assets/stylesheets/less/_common.less
+++ app/assets/stylesheets/less/_common.less
@@ -304,3 +304,5 @@
 .fixed-height-my-issues-list {
     line-height: 36px;
 }
+
+.dimgray { color:dimgray; }
(No newline at end of file)
app/assets/stylesheets/less/_page.less
--- app/assets/stylesheets/less/_page.less
+++ app/assets/stylesheets/less/_page.less
@@ -3074,6 +3074,14 @@
                     background-color: #FFCCBC;
                     color: #F4511E;
                 }
+                &.label-added {
+                    background-color: #B2EBF2;
+                    color: #0097A7;
+                }
+                &.label-deleted {
+                    background-color: #FFCCBC;
+                    color: #F4511E;
+                }
              }
 
              em { font-size:12px; color:#7F8C8D; margin-right:5px;}
app/controllers/IssueApp.java
--- app/controllers/IssueApp.java
+++ app/controllers/IssueApp.java
@@ -1,7 +1,7 @@
 /**
  * Yona, 21st Century Project Hosting SW
  * <p>
- * Copyright Yona & Yobi Authors & NAVER Corp.
+ * Copyright Yona & Yobi Authors & NAVER Corp. & NAVER LABS Corp.
  * https://yona.io
  **/
 package controllers;
@@ -424,17 +424,35 @@
 
     private static void updateLabelIfChanged(List<Long> attachingLabelIds, List<Long> detachingLabelIds,
                                              Issue issue) {
+        boolean isLabelChanged = false;
+        StringBuilder addedLabels = new StringBuilder();
+        StringBuilder deletedLabels = new StringBuilder();
+
         if (attachingLabelIds != null) {
             for (Long labelId : attachingLabelIds) {
-                issue.labels.add(IssueLabel.finder.byId(labelId));
+                IssueLabel label = IssueLabel.finder.byId(labelId);
+                issue.labels.add(label);
+                isLabelChanged = true;
+                addedLabels.append(label.category.name).append(" - ").append(label.name);
             }
         }
 
         if (detachingLabelIds != null) {
             for (Long labelId : detachingLabelIds) {
-                issue.labels.remove(IssueLabel.finder.byId(labelId));
+                IssueLabel label = IssueLabel.finder.byId(labelId);
+                issue.labels.remove(label);
+                isLabelChanged = true;
+                deletedLabels.append(label.category.name).append(" - ").append(label.name);
             }
         }
+
+        if(isLabelChanged) {
+            NotificationEvent notiEvent = NotificationEvent.afterIssueLabelChanged(
+                    addedLabels.toString(),
+                    deletedLabels.toString(),
+                    issue);
+            IssueEvent.addFromNotificationEventWithoutSkipEvent(notiEvent, issue, UserApp.currentUser().loginId);
+        }
     }
 
     private static void updateMilestoneIfChanged(Milestone milestone, Issue issue) {
app/models/NotificationEvent.java
--- app/models/NotificationEvent.java
+++ app/models/NotificationEvent.java
@@ -201,6 +201,12 @@
                 } else if (StringUtils.isNotBlank(oldValue)) {
                     return Messages.get(lang, "notification.issue.sharer.deleted");
                 }
+            case ISSUE_LABEL_CHANGED:
+                if (StringUtils.isNotBlank(newValue)) {
+                    return Messages.get(lang, "notification.issue.label.added", User.findByLoginId(newValue).getDisplayName());
+                } else if (StringUtils.isNotBlank(oldValue)) {
+                    return Messages.get(lang, "notification.issue.label.deleted");
+                }
             default:
                 play.Logger.error("Unknown event message: " + this);
                 return null;
@@ -893,6 +899,18 @@
         return receivers;
     }
 
+    public static NotificationEvent afterIssueLabelChanged(String addedLabels, String deletedLabels, Issue issue) {
+        NotificationEvent notiEvent = createFromCurrentUser(issue);
+        notiEvent.title = formatReplyTitle(issue);
+        notiEvent.receivers = null; // no receivers
+        notiEvent.eventType = ISSUE_LABEL_CHANGED;
+        notiEvent.oldValue = deletedLabels;
+        notiEvent.newValue = addedLabels;
+
+        NotificationEvent.addWithoutSkipEvent(notiEvent);
+        return notiEvent;
+    }
+
     private static Set<User> getReceiversForIssueBodyChanged(String oldBody, Issue issue) {
         Set<User> receivers = issue.getWatchers();
         receivers.addAll(getNewMentionedUsers(oldBody, issue.body));
app/models/enumeration/EventType.java
--- app/models/enumeration/EventType.java
+++ app/models/enumeration/EventType.java
@@ -35,7 +35,8 @@
     ORGANIZATION_MEMBER_ENROLL_REQUEST("notification.organization.type.member.enroll",19),
     COMMENT_UPDATED("notification.type.comment.updated", 20),
     ISSUE_MOVED("notification.type.issue.is.moved", 21),
-    ISSUE_SHARER_CHANGED("notification.type.issue.sharer.changed", 22);
+    ISSUE_SHARER_CHANGED("notification.type.issue.sharer.changed", 22),
+    ISSUE_LABEL_CHANGED("notification.type.issue.label.changed", 23);
 
     private String descr;
 
app/views/issue/partial_comments.scala.html
--- app/views/issue/partial_comments.scala.html
+++ app/views/issue/partial_comments.scala.html
@@ -32,6 +32,23 @@
     }
 }
 
+@issueLabelBox(categoryAndName: String, project:Project ) = @{
+    val splitedCategoryAndName = categoryAndName.split(" - ")
+    if(splitedCategoryAndName.length != 2) {
+        categoryAndName
+    } else {
+        var categoryName = splitedCategoryAndName(0).trim
+        var labelName = splitedCategoryAndName(1).trim
+        val issueLabel = IssueLabel.findByName(labelName, categoryName, project)
+        if( issueLabel != null) {
+            val labelColor = issueLabel.color
+            s"<div class='label issue-label' style='background-color: $labelColor'>$labelName</div>"
+        } else {
+            labelName
+        }
+    }
+}
+
 @assginedMesssage(newValue: String, user:User) = @{
     val LoginId = user.loginId
     newValue match {
@@ -187,6 +204,15 @@
                                 @Html(Messages("issue.event.sharer.deleted", linkToUser(user.loginId, user.getPureNameOnly), linkToUser(event.oldValue, User.findByLoginId(event.oldValue).getPureNameOnly)))
                             }
                         }
+                        case EventType.ISSUE_LABEL_CHANGED => {
+                            @if(StringUtils.isBlank(event.oldValue) && StringUtils.isNotBlank(event.newValue)){
+                                <span class="state label-added">@Messages("issue.event.label.added.title")</span>
+                                @Html(Messages("issue.event.label.added", linkToUser(user.loginId, user.getPureNameOnly), issueLabelBox(event.newValue, project)))
+                            } else {
+                                <span class="state label-deleted">@Messages("issue.event.label.deleted.title")</span>
+                                @Html(Messages("issue.event.label.deleted", linkToUser(user.loginId, user.getPureNameOnly), issueLabelBox(event.oldValue, project)))
+                            }
+                        }
                         case _ => {
                             @event.newValue by @linkToUser(user.loginId, user.getDisplayName)
                         }
app/views/issue/view.scala.html
--- app/views/issue/view.scala.html
+++ app/views/issue/view.scala.html
@@ -567,6 +567,13 @@
             } else {
                 $yobi.notify('@Messages("site.features.error.clipboard")', 1500);
             }
+
+            // timeline label text color adjusting
+            $(".event > .label").each(function() {
+                var $this = $(this);
+                $this.removeClass("dimgray white")
+                        .addClass($yobi.getContrastColor($this.css('background-color')))
+            });
         });
 </script>
 }
conf/messages
--- conf/messages
+++ conf/messages
@@ -279,6 +279,10 @@
 issue.event.assigned = {0} assigned this issue to {1}
 issue.event.assignedToMe = {0} assigned this issue to oneself
 issue.event.closed = {0} closed this issue
+issue.event.label.added = {0} added {1} label
+issue.event.label.added.title = Added
+issue.event.label.deleted = {0} removed {1} label
+issue.event.label.deleted.title = Removed
 issue.event.moved = {0} moved this issue from {1}
 issue.event.moved.title = moved
 issue.event.open = {0} reopened this issue
@@ -408,6 +412,8 @@
 notification.help.update.pullrequest = when the status of pull request is changed.
 notification.issue.assigned = Issue has been assigned to {0}
 notification.issue.closed  = Issue has been closed
+notification.issue.label.added = Label {0} is added
+notification.issue.label.deleted = Label {0} is deleted
 notification.issue.reopened = Issue has been reopened
 notification.issue.unassigned = Issue has been unassigned
 notification.issue.sharer.added = Issue is shared with {0}
@@ -450,6 +456,7 @@
 notification.type.issue.assignee.changed = Issue assignee changed.
 notification.type.issue.body.changed = Issue body changed
 notification.type.issue.is.moved = Issue moved
+notification.type.issue.label.changed = Issue label changed
 notification.type.issue.moved = Issue has been moved. From {0} To {1}
 notification.type.issue.referred.from.commit = Issue mentioned in commit
 notification.type.issue.referred.from.pullrequest = Issue mentioned in pull request
conf/messages.ko-KR
--- conf/messages.ko-KR
+++ conf/messages.ko-KR
@@ -279,6 +279,10 @@
 issue.event.assigned = {0}님이 {1}님을 이 이슈의 담당자로 지정하였습니다.
 issue.event.assignedToMe = {0}님이 자신을 이슈의 담당자로 지정하였습니다.
 issue.event.closed = {0}님이 이 이슈를 닫았습니다.
+issue.event.label.added = {0} 님이 {1} 라벨을 추가했습니다.
+issue.event.label.added.title = 추가
+issue.event.label.deleted = {0} 님이 {1} 라벨을 제거했습니다.
+issue.event.label.deleted.title = 삭제
 issue.event.moved = {0}님이 {1}로부터 이슈를 이동했습니다.
 issue.event.moved.title = 이동함
 issue.event.open = {0}님이 이 이슈를 다시 열었습니다.
@@ -408,6 +412,8 @@
 notification.help.update.pullrequest = 보낸 코드의 상태가 변경 될 때
 notification.issue.assigned = {0}에게 이슈 할당됨
 notification.issue.closed  = 이슈 닫힘
+notification.issue.label.added = {0} 라벨 추가되었습니다
+notification.issue.label.deleted = {0} 라벨 삭제되었습니다
 notification.issue.reopened = 이슈 다시 열림
 notification.issue.sharer.added = {0} 님에게 이슈가 공유되었습니다.
 notification.issue.sharer.deleted = 이슈 공유 상태가 변경되었습니다.
@@ -450,6 +456,7 @@
 notification.type.issue.assignee.changed = 이슈 담당자 변경
 notification.type.issue.body.changed = 이슈 본문 변경
 notification.type.issue.is.moved = 이슈 이동
+notification.type.issue.label.changed = 이슈 라벨 변경
 notification.type.issue.moved = 이슈가 {0}에서 {1}로 이동되었습니다
 notification.type.issue.referred.from.commit = 커밋에서의 이슈 언급
 notification.type.issue.referred.from.pullrequest = 코드 주고받기에서의 이슈 언급
Add a comment
List