diff --git a/app/assets/stylesheets/less/_page.less b/app/assets/stylesheets/less/_page.less index ba14c1b00..0fbac9b60 100644 --- a/app/assets/stylesheets/less/_page.less +++ b/app/assets/stylesheets/less/_page.less @@ -6345,3 +6345,28 @@ div.diff-body[data-outdated="true"] tr:hover .icon-comment { .group-board { width: 100% !important; } + +.board-labels { + width: 400px; + display: inline-block; + dt { + display: none; + } +} + +.post-list{ + .search-wrap { + & form { + width: 90%; + .search-bar { + height: 22px; + width: 400px; + display: inline-block; + margin-right: 10px; + } + .select2-container { + height: 32px; + } + } + } +} diff --git a/app/controllers/BoardApi.java b/app/controllers/BoardApi.java new file mode 100644 index 000000000..b08614f5b --- /dev/null +++ b/app/controllers/BoardApi.java @@ -0,0 +1,51 @@ +/** + * Yobire, Project Hosting SW + * + * @author Suwon Chae + * Copyright 2016 the original author or authors. + */ + +package controllers; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import models.IssueLabel; +import models.Posting; +import models.Project; +import play.db.ebean.Transactional; +import play.libs.Json; +import play.mvc.Result; + +import java.util.HashSet; +import java.util.Set; + +import static play.libs.Json.toJson; + +public class BoardApi extends AbstractPostingApp { + + @Transactional + public static Result updatePostLabel(String owner, String projectName, Long number) { + JsonNode json = request().body().asJson(); + if(json == null) { + return badRequest("Expecting Json data"); + } + Project project = Project.findByOwnerAndProjectName(owner, projectName); + Posting posting = Posting.findByNumber(project, number); + Set labels = new HashSet<>(); + + for(JsonNode node: json){ + System.out.println("node: " + node); + Long labelId = Long.parseLong(node.asText()); + labels.add(IssueLabel.finder.byId(labelId)); + } + + posting.labels = labels; + posting.save(); + + ObjectNode result = Json.newObject(); + result.put("id", project.owner); + result.put("labels", toJson(posting.labels.size())); + return ok(result); + } + +} diff --git a/app/controllers/BoardApp.java b/app/controllers/BoardApp.java index 116aca748..952ac0eef 100644 --- a/app/controllers/BoardApp.java +++ b/app/controllers/BoardApp.java @@ -21,21 +21,17 @@ package controllers; import actions.NullProjectCheckAction; - import com.avaje.ebean.ExpressionList; import com.avaje.ebean.Page; - +import com.fasterxml.jackson.databind.node.ObjectNode; import controllers.annotation.AnonymousCheck; import controllers.annotation.IsAllowed; import controllers.annotation.IsCreatable; import models.*; import models.enumeration.Operation; import models.enumeration.ResourceType; - import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import com.fasterxml.jackson.databind.node.ObjectNode; - import play.data.Form; import play.db.ebean.Transactional; import play.libs.Json; @@ -54,15 +50,15 @@ import javax.annotation.Nonnull; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +import java.util.*; import static com.avaje.ebean.Expr.icontains; public class BoardApp extends AbstractPostingApp { public static class SearchCondition extends AbstractPostingApp.SearchCondition { public List projectNames; + public String [] labelIds; + public Set labelIdSet = new HashSet<>(); private ExpressionList asExpressionList(Project project) { ExpressionList el = Posting.finder.where().eq("project.id", project.id); @@ -70,6 +66,11 @@ private ExpressionList asExpressionList(Project project) { el.or(icontains("title", filter), icontains("body", filter)); } + if (CollectionUtils.isNotEmpty(labelIdSet)) { + Set labels = IssueLabel.finder.where().idIn(new ArrayList<>(labelIdSet)).findSet(); + el.in("id", Posting.finder.where().in("labels", labels).findIds()); + } + if (StringUtils.isNotBlank(orderBy)) { el.orderBy(orderBy + " " + orderDir); } @@ -155,6 +156,8 @@ public static Result posts(String userName, String projectName, int pageNum) { if (searchCondition.orderBy.equals("id")) { searchCondition.orderBy = "createdDate"; } + searchCondition.labelIdSet.addAll(LabelApp.getLabelIds(request())); + searchCondition.labelIdSet.remove(null); ExpressionList el = searchCondition.asExpressionList(project); el.eq("notice", false); diff --git a/app/models/IssueLabel.java b/app/models/IssueLabel.java index c86dbd4b1..2d302b03f 100644 --- a/app/models/IssueLabel.java +++ b/app/models/IssueLabel.java @@ -67,6 +67,9 @@ public IssueLabelException(String s) { @ManyToMany(mappedBy="labels", fetch = FetchType.EAGER) public Set issues; + @ManyToMany(mappedBy="labels", fetch = FetchType.EAGER) + public Set postings; + public static List findByProject(Project project) { return finder.where() .eq("project.id", project.id) diff --git a/app/models/Posting.java b/app/models/Posting.java index 2589e313f..a5db3f487 100644 --- a/app/models/Posting.java +++ b/app/models/Posting.java @@ -10,7 +10,9 @@ import javax.persistence.*; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import static com.avaje.ebean.Expr.eq; @@ -27,6 +29,19 @@ public class Posting extends AbstractPosting { @OneToMany(cascade = CascadeType.ALL) public List comments; + @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.REMOVE) + public Set labels; + + public Set getLabelIds() { + Set labelIds = new HashSet<>(); + + for(IssueLabel label : this.labels){ + labelIds.add(label.id); + } + + return labelIds; + } + public Posting(Project project, User author, String title, String body) { super(project, author, title, body); } diff --git a/app/views/board/list.scala.html b/app/views/board/list.scala.html index 0753049f6..7d4d2f65a 100644 --- a/app/views/board/list.scala.html +++ b/app/views/board/list.scala.html @@ -30,24 +30,30 @@ @makeFilterLink(fieldName:String, orderBy:String, orderDir:String, fieldText:String) = { @if(orderBy.equals(fieldName)) { - @fieldText + @fieldText } else { - @fieldText + @fieldText } } @projectLayout(title, project, utils.MenuType.BOARD) { @projectMenu(project, utils.MenuType.BOARD, "main-menu-only") +
-
+
-
+ +
+ @if(!IssueLabel.findByProject(project).isEmpty){ + @issue.partial_select_label(IssueLabel.findByProject(project), param.labelIdSet) + } +
@Messages("post.write") @@ -107,6 +113,11 @@ "N": "@routes.BoardApp.newPostForm(project.owner, project.name)" }); } + + $('.board-labels select').on('change', function(e){ + $("#option_form").submit(); + }); }); +@common.select2() } diff --git a/app/views/board/partial_list.scala.html b/app/views/board/partial_list.scala.html index f86a6255f..84b124fb2 100644 --- a/app/views/board/partial_list.scala.html +++ b/app/views/board/partial_list.scala.html @@ -64,7 +64,10 @@ @countHtml("comments",routes.BoardApp.post(project.owner, project.name, post.getNumber).toString() + "#comments", post.numOfComments) - } + } + @for(label <- post.labels) { + @label.name + }
} diff --git a/app/views/board/view.scala.html b/app/views/board/view.scala.html index feb30d4f5..15246a239 100644 --- a/app/views/board/view.scala.html +++ b/app/views/board/view.scala.html @@ -5,6 +5,7 @@ * http://yobi.io * * @author Ahn Hyeok Jun +* @author Suwon Chae * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -65,9 +66,18 @@ @Messages("common.noAuthor") } +
+ @if(!IssueLabel.findByProject(project).isEmpty){ + @if(isAllowed(UserApp.currentUser(), post.asResource(), Operation.UPDATE)){ + @issue.partial_select_label(IssueLabel.findByProject(project), post.getLabelIds) + } else { + @issue.partial_show_selected_label(post.labels.toList, "") + } + } +
-
@Html(Markdown.render(post.body, post.asResource().getProject()))
-
+
@Html(Markdown.render(post.body, post.asResource().getProject()))
+