Yi EungJun 2012-11-19
issue: Apply new UI and add Label feature.
And remove State and rename StateType to State.
@ed636e734dbff6075601fd352d793a27c5734602
app/controllers/IssueApp.java
--- app/controllers/IssueApp.java
+++ app/controllers/IssueApp.java
@@ -14,6 +14,7 @@
 import views.html.issue.*;
 
 import java.io.*;
+import java.util.Set;
 
 public class IssueApp extends Controller {
 
@@ -22,16 +23,16 @@
      *
      * @param projectName
      *            프로젝트 이름
-     * @param statusType
+     * @param state
      *            이슈 해결 상태
      * @return
      */
-    public static Result issues(String userName, String projectName, String stateType) {
+    public static Result issues(String userName, String projectName, String state) {
         Project project = ProjectApp.getProject(userName, projectName);
         Form<SearchCondition> issueParamForm = new Form<SearchCondition>(SearchCondition.class);
         SearchCondition issueParam = issueParamForm.bindFromRequest().get();
-        Page<Issue> issues = Issue.find(project.name, issueParam.pageNum,
-                StateType.getValue(stateType), issueParam.sortBy,
+        Page<Issue> issues = Issue.find(project.id, issueParam.pageNum,
+                State.getValue(state), issueParam.sortBy,
                 Direction.getValue(issueParam.orderBy), issueParam.filter, issueParam.milestone,
                 issueParam.commentedCheck);
         return ok(issueList.render("title.issueList", issues, issueParam, project));
@@ -43,6 +44,9 @@
         if (issueInfo == null) {
             return ok(notExistingPage.render("title.post.notExistingPage", project));
         } else {
+            for (IssueLabel label: issueInfo.labels) {
+              label.refresh();
+            }
             Form<IssueComment> commentForm = new Form<IssueComment>(IssueComment.class);
             Issue targetIssue = Issue.findById(issueId);
             Form<Issue> editForm = new Form<Issue>(Issue.class).fill(targetIssue);
@@ -65,15 +69,22 @@
             newIssue.authorId = UserApp.currentUser().id;
             newIssue.authorName = UserApp.currentUser().name;
             newIssue.project = project;
-            newIssue.state = IssueState.ENROLLED;
-            newIssue.updateStateType(newIssue);
+            newIssue.state = State.OPEN;
+
+            String[] labelIds = request().body().asMultipartFormData().asFormUrlEncoded().get("labelIds[]");
+            if (labelIds != null) {
+                for (String labelId: labelIds) {
+                    newIssue.labels.add(IssueLabel.findById(Long.parseLong(labelId)));
+                }
+            }
+
             Long issueId = Issue.create(newIssue);
 
             // Attach all of the files in the current user's temporary storage.
             Attachment.attachFiles(UserApp.currentUser().id, project.id, Resource.ISSUE_POST, issueId);
         }
         return redirect(routes.IssueApp.issues(project.owner, project.name,
-                StateType.ALL.stateType()));
+                State.ALL.state()));
     }
 
     public static Result editIssue(String userName, String projectName, Long id) {
@@ -93,13 +104,12 @@
             issue.id = id;
             issue.date = Issue.findById(id).date;
             issue.project = project;
-            issue.updateState(issue);
             Issue.edit(issue);
 
             // Attach the files in the current user's temporary storage.
             Attachment.attachFiles(UserApp.currentUser().id, project.id, Resource.ISSUE_POST, id);
         }
-        return redirect(routes.IssueApp.issues(project.owner, project.name, StateType.ALL.name()));
+        return redirect(routes.IssueApp.issues(project.owner, project.name, State.ALL.name()));
     }
 
     public static Result deleteIssue(String userName, String projectName, Long issueId) {
@@ -107,7 +117,7 @@
 
         Issue.delete(issueId);
         return redirect(routes.IssueApp.issues(project.owner, project.name,
-                StateType.ALL.stateType()));
+                State.ALL.state()));
     }
 
     public static Result saveComment(String userName, String projectName, Long issueId) throws IOException {
@@ -140,16 +150,16 @@
         return redirect(routes.IssueApp.issue(project.owner, project.name, issueId));
     }
 
-    public static Result extractExcelFile(String userName, String projectName, String stateType)
+    public static Result extractExcelFile(String userName, String projectName, String state)
             throws Exception {
         Project project = ProjectApp.getProject(userName, projectName);
         Form<SearchCondition> issueParamForm = new Form<SearchCondition>(SearchCondition.class);
         SearchCondition issueParam = issueParamForm.bindFromRequest().get();
-        Page<Issue> issues = Issue.find(project.name, issueParam.pageNum,
-                StateType.getValue(stateType), issueParam.sortBy,
+        Page<Issue> issues = Issue.find(project.id, issueParam.pageNum,
+                State.getValue(state), issueParam.sortBy,
                 Direction.getValue(issueParam.orderBy), issueParam.filter, issueParam.milestone,
                 issueParam.commentedCheck);
-        Issue.excelSave(issues.getList(), project.name + "_" + stateType + "_filter_"
+        Issue.excelSave(issues.getList(), project.name + "_" + state + "_filter_"
                 + issueParam.filter + "_milestone_" + issueParam.milestone);
         return ok(issueList.render("title.issueList", issues, issueParam, project));
     }
@@ -162,4 +172,4 @@
     public static Result getIssueDatil(){
     	return TODO;
     }
-}
(No newline at end of file)
+}
 
app/controllers/IssueLabelApp.java (added)
+++ app/controllers/IssueLabelApp.java
@@ -0,0 +1,89 @@
+package controllers;
+
+import java.util.List;
+import java.util.Map;
+
+import controllers.ProjectApp;
+import models.IssueLabel;
+import models.Project;
+import models.Issue;
+import models.enumeration.Operation;
+import models.enumeration.Resource;
+import play.mvc.Controller;
+import play.mvc.Result;
+import utils.AccessControl;
+import utils.RequestUtil;
+import static play.libs.Json.toJson;
+import play.Logger;
+
+import play.data.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class IssueLabelApp extends Controller {
+
+    public static Result getAll(String userName, String projectName) {
+        Project project = ProjectApp.getProject(userName, projectName);
+
+        List<Map<String, String>> labels = new ArrayList<Map<String, String>>();
+        for (IssueLabel label : IssueLabel.findByProjectId(project.id)) {
+            Map<String, String> labelPropertyMap = new HashMap<String, String>();
+            labelPropertyMap.put("id", "" + label.id);
+            labelPropertyMap.put("category", label.category);
+            labelPropertyMap.put("color", label.color);
+            labelPropertyMap.put("name", label.name);
+            labels.add(labelPropertyMap);
+        }
+
+        response().setHeader("Content-Type", "application/json");
+        return ok(toJson(labels));
+    }
+
+    public static Result post(String userName, String projectName) {
+        Form<IssueLabel> labelForm = new Form<IssueLabel>(IssueLabel.class).bindFromRequest();
+
+        IssueLabel label = labelForm.get();
+        label.project = ProjectApp.getProject(userName, projectName);
+
+        if (label.exists()) {
+            return ok();
+        } else {
+            label.save();
+
+            response().setHeader("Content-Type", "application/json");
+
+            Map<String, String> labelPropertyMap = new HashMap<String, String>();
+            labelPropertyMap.put("id", "" + label.id);
+            labelPropertyMap.put("name", label.name);
+            labelPropertyMap.put("color", label.color);
+            labelPropertyMap.put("category", label.category);
+
+            return created(toJson(labelPropertyMap));
+        }
+    }
+
+    public static Result delete(String userName, String projectName, Long id) {
+        // _method must be 'delete'
+        DynamicForm bindedForm = form().bindFromRequest();
+        if (!bindedForm.get("_method").toLowerCase()
+                .equals("delete")) {
+            return badRequest("_method must be 'delete'.");
+        }
+
+        IssueLabel label = IssueLabel.findById(id);
+
+        if (label == null) {
+            Logger.debug(id.toString());
+            return notFound();
+        }
+
+        if (!AccessControl.isAllowed(UserApp.currentUser().id, label.project.id,
+            Resource.ISSUE_LABEL, Operation.DELETE, label.id)) {
+            return forbidden();
+        }
+
+        label.delete();
+
+        return ok();
+    }
+}
app/controllers/MilestoneApp.java
--- app/controllers/MilestoneApp.java
+++ app/controllers/MilestoneApp.java
@@ -32,7 +32,7 @@
         MilestoneCondition mCondition = form(MilestoneCondition.class).bindFromRequest().get();
 
         List<Milestone> milestones = Milestone.findMilestones(project.id,
-                StateType.getValue(mCondition.state),
+                State.getValue(mCondition.state),
                 mCondition.sort,
                 Direction.getValue(mCondition.direction));
         return ok(list.render("title.milestoneList", milestones, project, mCondition));
@@ -70,7 +70,7 @@
         }
         MilestoneCondition mCondition = form(MilestoneCondition.class).bindFromRequest().get();
         List<Milestone> milestones = Milestone.findMilestones(project.id,
-                StateType.ALL,
+                State.ALL,
                 mCondition.sort,
                 Direction.getValue(mCondition.direction));
         return ok(manage.render("title.milestoneManage", milestones, project, mCondition));
app/models/Issue.java
--- app/models/Issue.java
+++ app/models/Issue.java
@@ -27,20 +27,14 @@
  * @param id              이슈 ID
  * @param title           이슈 제목
  * @param body            이슈 내용
- * @param state           이슈 상태(등록, 진행중, 해결, 닫힘)
- * @param statusType      이슈 상태, 등록 및 진행중 => 미해결, 해결 및 닫힘 => 해결
+ * @param state           이슈 상태(열림, 닫힘)
  * @param date            이슈 등록 날짜
  * @param authorId        이슈 작성자 ID
  * @param project         이슈가 등록된 프로젝트
  * @param issueType       이슈 상세정보의 유형
  * @param assigneeId      이슈에 배정된 담당자 Id
- * @param componentName   컴포넌트
  * @param milestone       이슈가 등록된 마일스톤
  * @param importance      이슈 상세정보의 중요도
- * @param diagnosisResult 이슈 상세정보의 진단유형
- * @param osType          이슈 상세정보의 OS 유형
- * @param browserType     이슈 상세정보의 브라우저 유형
- * @param dbmsType        이슈 상세정보의 DBMS 유형
  * @author Taehyun Park
  *         <p/>
  *         Issue entity mangaed by Ebean
@@ -75,16 +69,7 @@
     public Long assigneeId;
     public Long authorId;
     public String authorName;
-    public IssueState state;
-    public StateType stateType;
-    public String issueType;
-    public String componentName;
-    public String osType;
-    public String browserType;
-    public String dbmsType;
-    public String importance;
-    public String diagnosisResult;
-
+    public State state;
     @OneToMany
 	public List<IssueDetail> issueDetails;
 
@@ -94,12 +79,11 @@
     @OneToMany(mappedBy = "issue", cascade = CascadeType.ALL)
     public List<IssueComment> comments;
 
+    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
+    public Set<IssueLabel> labels;
+
     public Issue(String title) {
         this.date = JodaDateUtil.now();
-    }
-
-    public String issueTypeLabel() {
-        return issueTypes().get(issueType);
     }
 
     public Duration ago() {
@@ -117,116 +101,6 @@
     public String assigneeName() {
 
         return (this.assigneeId != null ? User.findNameById(this.assigneeId) : "issue.noAssignee");
-    }
-
-    /**
-     * 이슈의 오픈 상태를 확인한다.
-     *
-     * @return boolean
-     */
-    public boolean isOpen() {
-        return StateType.OPEN.equals(this.stateType);
-    }
-
-    /**
-     * View에서 스트링값으로 변환하도록 한다.
-     *
-     * @return
-     */
-
-    /**
-     * 해당 이슈의 상태(state) 따라서 탭 기능에서 구분 짖는(stateType) 것이 해결인지 미해결인지 값을 결정해준다.
-     * diagnosisResult가 2인 경우는 현 진단결과 선택 란에서 "수정완료"를 의미하므로, 이슈 정상 해결을 의미한다.
-     *
-     * @param state
-     */
-    public void updateStateType(Issue issue) {
-
-        if (this.state == null || this.state.equals(IssueState.ASSIGNED)
-                || this.state.equals(IssueState.ENROLLED)) {
-            this.stateType = StateType.OPEN;
-        } else {
-            this.stateType = StateType.CLOSED;
-        }
-    }
-
-    /**
-     * 이슈의 담당자(assignee) 배정과 이슈의 진단결과(diagnosisResult)에 따라 이슈의 상태를 정해진 로직에 따라
-     * 변경한다.
-     *
-     * @param issue
-     */
-    public void updateState(Issue issue) {
-
-        if (isEnrolled(issue)) {
-            this.state = IssueState.ENROLLED;
-        } else if (isFinished(issue)) {
-            this.state = IssueState.FINISHED;
-        } else if (isAssigned(issue)) {
-            this.state = IssueState.ASSIGNED;
-        } else if (isSolved(issue)) {
-            this.state = IssueState.SOLVED;
-        } else
-            this.state = IssueState.ENROLLED;
-
-        updateStateType(issue);
-    }
-
-    /**
-     * 이슈가 담당자와 진단결과가 없는 경우에는 "등록" 상태임을 확인해준다.
-     *
-     * @param issue
-     * @return boolean
-     */
-    public boolean isEnrolled(Issue issue) {
-
-        if (issue.assigneeId == null && issue.diagnosisResult.equals("")) {
-            return true;
-        } else
-            return false;
-    }
-
-    /**
-     * 이슈가 담당자는 배정받고, 진단결과가 없는 경우에는 "진행중" 상태임을 확인해준다.
-     *
-     * @param issue
-     * @return
-     */
-    public boolean isAssigned(Issue issue) {
-
-        if (issue.assigneeId != null && issue.diagnosisResult.equals("")) {
-            return true;
-        } else
-            return false;
-    }
-
-    /**
-     * 이슈가 담당자를 배정받고 진단결과가 2번째 수정완료(추후 변경 가능) 인 경우에는 "해결" 상태임을 확인해준다.
-     *
-     * @param issue
-     * @return
-     */
-    public boolean isSolved(Issue issue) {
-
-        if (issue.assigneeId != null && issue.diagnosisResult.equals("2")) {
-            return true;
-        } else
-            return false;
-    }
-
-    /**
-     * 이슈가 담당자의 유무와 상관 없이, 진단결과가 2번째 수정완료가 아닌 다른 사유가 존재한 경우에는 "닫힘" 상태임을 확인해준다.
-     *
-     * @param issue
-     * @return
-     */
-
-    public boolean isFinished(Issue issue) {
-
-        if (!issue.diagnosisResult.equals("2") && !issue.diagnosisResult.equals("")) {
-            return true;
-        } else
-            return false;
     }
 
     /**
@@ -329,7 +203,6 @@
             Milestone milestone = Milestone.findById(issue.milestoneId);
             milestone.add(issue);
         }
-        issue.updateStateType(issue);
         return issue.id;
     }
 
@@ -353,28 +226,35 @@
      * @param issue
      */
     public static void edit(Issue issue) {
-        issue.updateStateType(issue);
         issue.update();
+    }
+
+    public static int countIssues(Long projectId, State state) {
+        if (state == State.ALL) {
+            return finder.where().eq("project.id", projectId).findRowCount();
+        } else {
+            return finder.where().eq("project.id", projectId).eq("state", state).findRowCount();
+        }
     }
 
     /**
      * 미해결 탭을 눌렀을 때, open 상태의 이슈들을 찾아준다..
      *
-     * @param projectName
+     * @param projectId
      * @return
      */
-    public static Page<Issue> findOpenIssues(String projectName) {
-        return Issue.findIssues(projectName, StateType.OPEN);
+    public static Page<Issue> findOpenIssues(Long projectId) {
+        return Issue.findIssues(projectId, State.OPEN);
     }
 
     /**
      * 해결 탭을 눌렀을 때, closed 상태의 이슈들을 찾아준다.
      *
-     * @param projectName
+     * @param projectId
      * @return
      */
-    public static Page<Issue> findClosedIssues(String projectName) {
-        return Issue.findIssues(projectName, StateType.CLOSED);
+    public static Page<Issue> findClosedIssues(Long projectId) {
+        return Issue.findIssues(projectId, State.CLOSED);
     }
 
     /**
@@ -384,54 +264,54 @@
      * @param state
      * @return
      */
-    public static Page<Issue> findIssues(String projectName, StateType state) {
-        return find(projectName, FIRST_PAGE_NUMBER, state, DEFAULT_SORTER, Direction.DESC,
+    public static Page<Issue> findIssues(Long projectId, State state) {
+        return find(projectId, FIRST_PAGE_NUMBER, state, DEFAULT_SORTER, Direction.DESC,
                 "", null, false);
     }
 
     /**
      * 검색창에서 제공된 query(filter)와 댓글과 파일첨부된 이슈만 찾아주는 체크박스의 값에 따라 필터링된 이슈들을 찾아준다.
      *
-     * @param projectName
+     * @param projectId
      * @param filter
      * @param state
      * @param commentedCheck
      * @return
      */
-    public static Page<Issue> findFilteredIssues(String projectName, String filter,
-                                                 StateType state, boolean commentedCheck) {
-        return find(projectName, FIRST_PAGE_NUMBER, state, DEFAULT_SORTER, Direction.DESC,
+    public static Page<Issue> findFilteredIssues(Long projectId, String filter,
+                                                 State state, boolean commentedCheck) {
+        return find(projectId, FIRST_PAGE_NUMBER, state, DEFAULT_SORTER, Direction.DESC,
                 filter, null, commentedCheck);
     }
 
     /**
      * 댓글이 달린 이슈들만 찾아준다.
      *
-     * @param projectName
+     * @param projectId
      * @param filter
      * @return
      */
-    public static Page<Issue> findCommentedIssues(String projectName, String filter) {
-        return find(projectName, FIRST_PAGE_NUMBER, StateType.ALL, DEFAULT_SORTER,
+    public static Page<Issue> findCommentedIssues(Long projectId, String filter) {
+        return find(projectId, FIRST_PAGE_NUMBER, State.ALL, DEFAULT_SORTER,
                 Direction.DESC, filter, null, true);
     }
 
     /**
      * 마일스톤 Id에 의거해서 해당 마일스톤에 속한 이슈들을 찾아준다.
      *
-     * @param projectName
+     * @param projectId
      * @param milestoneId
      * @return
      */
-    public static Page<Issue> findIssuesByMilestoneId(String projectName, Long milestoneId) {
-        return find(projectName, FIRST_PAGE_NUMBER, StateType.ALL, DEFAULT_SORTER,
+    public static Page<Issue> findIssuesByMilestoneId(Long projectId, Long milestoneId) {
+        return find(projectId, FIRST_PAGE_NUMBER, State.ALL, DEFAULT_SORTER,
                 Direction.DESC, "", milestoneId, false);
     }
 
     /**
      * 이슈들을 아래의 parameter들의 조건에 의거하여 Page형태로 반환한다.
      *
-     * @param projectName       project ID to finder issues
+     * @param projectId       project ID to finder issues
      * @param pageNumber        Page to display
      * @param state             state type of issue(OPEN or CLOSED
      * @param sortBy            Issue property used for sorting, but, it might be fixed to
@@ -441,11 +321,11 @@
      * @param commentedCheck    filter applied on the commetedCheck column, 댓글이 존재하는 이슈만 필터링
      * @return 위의 조건에 따라 필터링된 이슈들을 Page로 반환.
      */
-    public static Page<Issue> find(String projectName, int pageNumber, StateType state,
+    public static Page<Issue> find(Long projectId, int pageNumber, State state,
                                    String sortBy, Direction order, String filter, Long milestoneId,
                                    boolean commentedCheck) {
         OrderParams orderParams = new OrderParams().add(sortBy, order);
-        SearchParams searchParams = new SearchParams().add("project.name", projectName,
+        SearchParams searchParams = new SearchParams().add("project.id", projectId,
                 Matching.EQUALS);
 
         if (filter != null && !filter.isEmpty()) {
@@ -459,14 +339,14 @@
         }
 
         if (state == null) {
-            state = StateType.ALL;
+            state = State.ALL;
         }
         switch (state) {
             case OPEN:
-                searchParams.add("stateType", StateType.OPEN, Matching.EQUALS);
+                searchParams.add("state", State.OPEN, Matching.EQUALS);
                 break;
             case CLOSED:
-                searchParams.add("stateType", StateType.CLOSED, Matching.EQUALS);
+                searchParams.add("state", State.CLOSED, Matching.EQUALS);
                 break;
             default:
         }
@@ -488,7 +368,7 @@
                 .findPagingList(condition.pageSize).getPage(condition.page - 1);
     }
 
-    public static Long findAssigneeIdByIssueId(String projectName, Long issueId) {
+    public static Long findAssigneeIdByIssueId(Long issueId) {
         return finder.byId(issueId).assigneeId;
     }
 
@@ -598,4 +478,12 @@
 		}
 		this.issueDetails.add(issueDetail);
 	}
+
+	public boolean isOpen() {
+	    return this.state == State.OPEN;
+	}
+
+	public boolean isClosed() {
+	    return this.state == State.CLOSED;
+	}
 }
 
app/models/IssueLabel.java (added)
+++ app/models/IssueLabel.java
@@ -0,0 +1,55 @@
+package models;
+
+import java.util.List;
+
+import javax.persistence.Id;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+
+import play.data.validation.Constraints.Required;
+import play.data.validation.*;
+import play.db.ebean.Model;
+
+import play.data.validation.*;
+import play.db.ebean.*;
+
+import javax.persistence.*;
+import java.util.*;
+
+@Entity
+public class IssueLabel extends Model {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = -35487506476718498L;
+    private static Finder<Long, IssueLabel> finder = new Finder<Long, IssueLabel>(Long.class, IssueLabel.class);
+
+    @Id
+    public Long id;
+
+    @Required
+    public String category;
+
+    @Required
+    public String color;
+
+    @Required
+    public String name;
+
+    @ManyToOne
+    public Project project;
+
+    public static List<IssueLabel> findByProjectId(Long projectId) {
+        return finder.where().eq("project.id", projectId).findList();
+    }
+
+    public static IssueLabel findById(Long id) {
+        return finder.byId(id);
+    }
+
+    public boolean exists() {
+        return finder.where().eq("project.id", project.id)
+                .eq("name", name).eq("color", color).findRowCount() > 0;
+    }
+}
app/models/Milestone.java
--- app/models/Milestone.java
+++ app/models/Milestone.java
@@ -78,7 +78,7 @@
      * @return
      */
     public static List<Milestone> findByProjectId(Long projectId) {
-        return Milestone.findMilestones(projectId, StateType.ALL);
+        return Milestone.findMilestones(projectId, State.ALL);
     }
 
     /**
@@ -88,7 +88,7 @@
      * @return
      */
     public static List<Milestone> findClosedMilestones(Long projectId) {
-        return Milestone.findMilestones(projectId, StateType.CLOSED);
+        return Milestone.findMilestones(projectId, State.CLOSED);
     }
 
     /**
@@ -98,7 +98,7 @@
      * @return
      */
     public static List<Milestone> findOpenMilestones(Long projectId) {
-        return Milestone.findMilestones(projectId, StateType.OPEN);
+        return Milestone.findMilestones(projectId, State.OPEN);
     }
 
     /**
@@ -119,7 +119,7 @@
      * @return
      */
     public static List<Milestone> findMilestones(Long projectId,
-                                                 StateType state) {
+                                                 State state) {
         return findMilestones(projectId, state, DEFAULT_SORTER, Direction.ASC);
     }
 
@@ -133,11 +133,11 @@
      * @return
      */
     public static List<Milestone> findMilestones(Long projectId,
-                                                 StateType state, String sort, Direction direction) {
+                                                 State state, String sort, Direction direction) {
         OrderParams orderParams = new OrderParams().add(sort, direction);
         SearchParams searchParams = new SearchParams().add("project.id", projectId, Matching.EQUALS);
         if (state == null) {
-            state = StateType.ALL;
+            state = State.ALL;
         }
         switch (state) {
             case OPEN:
@@ -163,7 +163,7 @@
      */
     public static Map<String, String> options(Long projectId) {
         LinkedHashMap<String, String> options = new LinkedHashMap<String, String>();
-        for (Milestone milestone : findMilestones(projectId, StateType.ALL, "title", Direction.ASC)) {
+        for (Milestone milestone : findMilestones(projectId, State.ALL, "title", Direction.ASC)) {
             options.put(milestone.id.toString(), milestone.title);
         }
         return options;
 
app/models/enumeration/IssueState.java (deleted)
--- app/models/enumeration/IssueState.java
@@ -1,24 +0,0 @@
-package models.enumeration;
-
-public enum IssueState {
-    ENROLLED("issue.state.enrolled"), ASSIGNED("issue.state.assigned"), SOLVED("issue.state.solved"), FINISHED("issue.state.finished");
-    private String state;
-
-    IssueState(String state) {
-        this.state = state;
-    }
-
-    public String state() {
-        return this.state;
-    }
-
-    public static IssueState getValue(String value) {
-        for (IssueState issueState : IssueState.values()) {
-            if (issueState.state().equals(value)) {
-                return issueState;
-            }
-        }
-        return IssueState.ENROLLED;
-    }
-   
-}
app/models/enumeration/Resource.java
--- app/models/enumeration/Resource.java
+++ app/models/enumeration/Resource.java
@@ -3,15 +3,12 @@
 public enum Resource {
     ISSUE_POST("issue_post"),
     ISSUE_COMMENT("issue_comment"),
-    ISSUE_ENVIRONMENT("issue_environment"),
     ISSUE_ASSIGNEE("issue_assignee"),
     ISSUE_STATE("issue_state"),
-    ISSUE_IMPORTANCE("issue_importance"),
     ISSUE_CATEGORY("issue_category"),
     ISSUE_MILESTONE("issue_milestone"),
-    ISSUE_COMPONENT("issue_component"),
-    ISSUE_DIAGNOSISRESULT("issue_diagnosisResult"),
     ISSUE_NOTICE("issue_notice"),
+    ISSUE_LABEL("issue_label"),
     BOARD_POST("board_post"),
     BOARD_COMMENT("board_comment"),
     BOARD_CATEGORY("board_category"),
@@ -24,15 +21,15 @@
     USER("user");
 
     private String resource;
-    
+
     Resource(String resource) {
         this.resource = resource;
     }
-    
+
     public String resource() {
         return this.resource;
     }
-    
+
     public static Resource getValue(String value) {
         for (Resource resource : Resource.values()) {
             if (resource.resource().equals(value)) {
 
app/models/enumeration/State.java (added)
+++ app/models/enumeration/State.java
@@ -0,0 +1,25 @@
+package models.enumeration;
+
+public enum State {
+    ALL("all"), OPEN("open"), CLOSED("closed");
+    private String state;
+
+    State(String state) {
+        this.state = state;
+    }
+
+    public String state() {
+        return this.state;
+    }
+
+    public static State getValue(String value) {
+        for (State issueState : State.values()) {
+            if (issueState.state().equals(value)) {
+                return issueState;
+            }
+        }
+        return State.OPEN;
+    }
+}
+
+
 
app/models/enumeration/StateType.java (deleted)
--- app/models/enumeration/StateType.java
@@ -1,25 +0,0 @@
-package models.enumeration;
-
-public enum StateType {
-    ALL("all"), OPEN("open"), CLOSED("closed");
-    private String stateType;
-
-    StateType(String stateType) {
-        this.stateType = stateType;
-    }
-
-    public String stateType() {
-        return this.stateType;
-    }
-
-    public static StateType getValue(String value) {
-        for (StateType issueStateType : StateType.values()) {
-            if (issueStateType.stateType().equals(value)) {
-                return issueStateType;
-            }
-        }
-        return StateType.OPEN;
-    }
-}
-
-
app/models/support/SearchCondition.java
--- app/models/support/SearchCondition.java
+++ app/models/support/SearchCondition.java
@@ -2,7 +2,7 @@
 
 import models.enumeration.*;
 /**
- * 
+ *
  * @author Taehyun Park
  *
  */
@@ -12,8 +12,8 @@
     public String sortBy;
     public String orderBy;
     public int pageNum;
-    
-    public String stateType;
+
+    public String state;
     public Boolean commentedCheck;
     public Boolean fileAttachedCheck;
     public Long milestone;
@@ -24,9 +24,9 @@
         orderBy = Direction.DESC.direction();
         pageNum = 0;
         milestone = null;
-        stateType = StateType.OPEN.name();
+        state = State.OPEN.name();
         commentedCheck = false;
         fileAttachedCheck = false;
-        
+
     }
 }
app/utils/AccessControl.java
--- app/utils/AccessControl.java
+++ app/utils/AccessControl.java
@@ -73,6 +73,8 @@
                 break;
             case USER:
                 return userId == resourceId;
+            case ISSUE_LABEL:
+                return ProjectUser.isMember(userId, projectId);
             default:
                 isAuthorEditible = false;
                 break;
app/views/issue/editIssue.scala.html
--- app/views/issue/editIssue.scala.html
+++ app/views/issue/editIssue.scala.html
@@ -88,16 +88,6 @@
                             '_default -> Messages("issue.new.selectDefault.assignee"),
                             '_showConstraints -> false)
                     }        
-            <!-- FIXME 컴포넌트 관련 스펙이 정해지면 수정할것 -->
-                    @isVisible(models.enumeration.Resource.ISSUE_COMPONENT) {  
-                        @select(
-                            issueForm("componentName"), 
-                            options = options(
-                                        "component_1"->"Component id 1"),
-                            '_label-> Messages("issue.new.detailInfo.component"),
-                            '_default -> Messages("issue.new.selectDefault.component"),
-                            '_showConstraints -> false)
-                     }
                      @isVisible(models.enumeration.Resource.ISSUE_MILESTONE) {
                         @select(
                             issueForm("milestoneId"), 
@@ -106,29 +96,6 @@
                             '_default -> Messages("issue.new.selectDefault.milestone"),
                             '_showConstraints -> false)
                      }
-                </div>      
-            </div>
-        </fieldset>
-        <fieldset>
-            <div class="well">
-                <legend><b>@Messages("issue.new.result")</b></legend>
-                <div class="well form-inline">
-                     @isVisible(models.enumeration.Resource.ISSUE_IMPORTANCE) {    
-                        @selectEx(
-                            issueForm("importance"), 
-                            options(Issue.importances),
-                            '_label-> Messages("issue.new.result.importance"),
-                            '_default -> Messages("issue.new.selectDefault.importance"),
-                            '_showConstraints -> false)
-                      }
-                      @isVisible(models.enumeration.Resource.ISSUE_DIAGNOSISRESULT) {      
-                        @selectEx(
-                            issueForm("diagnosisResult"), 
-                            options(Issue.diagnosisResults),
-                            '_label-> Messages("issue.new.result.diagnosisResult"),
-                            '_default -> Messages("issue.new.selectDefault.diagnosisResult"),
-                            '_showConstraints -> false)
-                       }     
                 </div>      
             </div>
         </fieldset>
app/views/issue/issue.scala.html
--- app/views/issue/issue.scala.html
+++ app/views/issue/issue.scala.html
@@ -11,6 +11,9 @@
   }
 }
 @main(Messages(title),project) {
+<style>
+@@IMPORT url("/assets/stylesheets/issue.css");
+</style>
 
 <div class="row">
 	<div class="span2 offset10">
@@ -33,6 +36,7 @@
 
 		<div class="span11">
 			<h1>@issue.title</h1>
+            <p>@for(label <- issue.labels) { <button class="issue-label" labelId="@label.id">@label.name</button> } </p>
 			<div>
 				<span>@issue.reporterName이 @agoString(issue.ago())에 작성함</span> |
                 <span>@issue.date</span>
@@ -51,39 +55,26 @@
 		<a class="btn pull-right" href=""><i class="icon-ok"></i>@Messages("button.autoNotification")</a>
 	</div>
 	</br></br>
+
     @if(session.get("userId") != null && ProjectUser.isMember(session.get("userId").toLong, project.id)){
        
 	   @form(action = routes.IssueApp.updateIssue(project.owner, project.name, issue.id), 
             'enctype -> "multipart/form-data",
             'class -> "form-horizontal") {
+
+            <!-- issue.label module make this as label selector -->
+            <fieldset class="labels"></fieldset>
+
             <fieldset>
 	            <div class="well">
 	                <legend><b>@Messages("issue.new.detailInfo")</b></legend>
 	                <div class="well form-inline">
-	                   @isVisible(models.enumeration.Resource.ISSUE_CATEGORY) {
-	                       @selectEx(
-	                            issueForm("issueType"), 
-	                            options(Issue.issueTypes),
-	                            '_label-> Messages("issue.new.detailInfo.issueType"),
-	                            '_default -> Messages("issue.new.selectDefault.issueType"),
-	                            '_showConstraints -> false)
-	                   }
 	                   @isVisible(models.enumeration.Resource.ISSUE_ASSIGNEE) {
 	                        @select(
 	                            issueForm("assigneeId"), 
 	                            options(ProjectUser.options(project.id)),
 	                            '_label-> Messages("issue.new.detailInfo.assignee"),
 	                            '_default -> Messages("issue.new.selectDefault.assignee"),
-	                            '_showConstraints -> false)
-	                   }
-	            <!-- FIXME 컴포넌트 관련 스펙이 정해지면 수정할것 -->
-	                   @isVisible(models.enumeration.Resource.ISSUE_COMPONENT) {                   
-	                        @select(
-	                            issueForm("componentName"), 
-	                            options = options(
-	                                        "component_1"->"Component id 1"),
-	                            '_label-> Messages("issue.new.detailInfo.component"),
-	                            '_default -> Messages("issue.new.selectDefault.component"),
 	                            '_showConstraints -> false)
 	                   }
 	                   @isVisible(models.enumeration.Resource.ISSUE_MILESTONE) {
@@ -97,30 +88,7 @@
 	                </div>      
 	            </div>
             </fieldset>
-            <fieldset>
-	            <div class="well">
-	                <legend><b>@Messages("issue.new.result")</b></legend>
-	                <div class="well form-inline">
-	                   @isVisible(models.enumeration.Resource.ISSUE_IMPORTANCE) {
-	                        @selectEx(
-	                            issueForm("importance"), 
-	                            options(Issue.importances),
-	                            '_label-> Messages("issue.new.result.importance"),
-	                            '_default -> Messages("issue.new.selectDefault.importance"),
-	                            '_showConstraints -> false)
-	                   }
-	                   @isVisible(models.enumeration.Resource.ISSUE_DIAGNOSISRESULT) {
-	                        @selectEx(
-	                            issueForm("diagnosisResult"), 
-	                            options(Issue.diagnosisResults),
-	                            '_label-> Messages("issue.new.result.diagnosisResult"),
-	                            '_default -> Messages("issue.new.selectDefault.diagnosisResult"),
-	                            '_showConstraints -> false)
-	                    }
-	                </div>      
-	            </div>
-            </fieldset>
-        <input type="hidden" value="@issue.title" name="title"/>
+           <input type="hidden" value="@issue.title" name="title"/>
         <input type="hidden" value="@issue.body" name="body"/>
         <input type="hidden" value="@issue.authorId" name="authorId"/>
         <div class = "btn pull-right">
@@ -205,5 +173,10 @@
 @views.html.markdown()
 <script type="text/javascript">
   nforge.require('shortcut.submit');
+  @for(label <- issue.labels) {
+    $('button.issue-label[labelId="@label.id"]').css('background-color', '@label.color');
+  }
+  nforge.require('nforge.issue.view');
+  nforge.require('issue.label', '@routes.IssueLabelApp.getAll(project.owner, project.name)', '@routes.IssueLabelApp.post(project.owner, project.name)', {editable: false});
 </script>
 }
app/views/issue/issueList.scala.html
--- app/views/issue/issueList.scala.html
+++ app/views/issue/issueList.scala.html
@@ -5,7 +5,7 @@
 
 @header(label:String, sortBy:String) = {
     <th>
-        <a class="th-sort" href="@routes.IssueApp.issues(project.owner, project.name, param.stateType)" data-sort-by="@sortBy">@label</a>
+        <a class="th-sort" href="@routes.IssueApp.issues(project.owner, project.name, param.state)" data-sort-by="@sortBy">@label</a>
         @if(sortBy == param.sortBy){
         @if(param.orderBy == "desc"){
         <i class="icon-chevron-down"></i>
@@ -14,105 +14,90 @@
         }
         }
     </th>
+
 }
 
 @main(Messages(title), project){
-<div id = "issue_page">
-	<div class="row">
-		<div span="span6">
-			<div class="pull-right">
-				<form id="searchForm" class="form-search form-inline" action="@routes.IssueApp.issues(project.owner, project.name, param.stateType)" method="GET" >
-					<select id="milestone" name="milestone">
-						<option class="blank" value ="">@Messages("issue.menu.milestoneSelectDefault")</option>@Milestone.options(project.id).map { v => <option id="changeMilestone" name ="milestone" value="@v._1" >@v._2</option>}
-					</select>
-					<input type="hidden" name="sortBy" value="@param.sortBy" class="h-value sort">
-          <input type="hidden" name="orderBy" value="@param.orderBy" class="h-value order">
-          <input type="hidden" name="stateType" value="@param.stateType">
-					<input type="text" name="filter" value="@param.filter" placeholder=@Messages("issue.menu.searchDefault")>
-					<button type="submit" id="searchSubmit" class="btn"> <i class="icon-search"> </i></button>
-				
-				<a class="btn btn-primary" href="@routes.IssueApp.newIssue(project.owner, project.name)" >@Messages("issue.menu.new")</a>
-				</form>
-			</div>
-		</div>
-	</div>
-	<div>
-		<ul class="nav nav-tabs" id="issue_list">
-			<li class="@if(param.stateType.equals("all")){active}">
-				<a href="@routes.IssueApp.issues(project.owner, project.name,"all")">@Messages("issue.stateType.all")</a></li>
-			<li class="@if(param.stateType.equals("open")){active}">
-				<a href="@routes.IssueApp.issues(project.owner, project.name, "open")">@Messages("issue.stateType.open")</a></li>
-			<li class="@if(param.stateType.equals("closed")){active}">
-				<a href="@routes.IssueApp.issues(project.owner, project.name, "closed")">@Messages("issue.stateType.closed")</a></li>
-		</ul>
-				
-		@if(currentPage.getTotalRowCount == 0){
-			<div class="well span11">
-				<em>@Messages("issue.is.empty")</em>
-			</div>
-		}else{
-		<div class="pull-right">
-			<form class="form-inline" id="checkboxForm" action="@routes.IssueApp.issues(project.owner, project.name, param.stateType)" method="GET">
-				
-				<input type="hidden" name="filter" value="@param.filter">
-				<input type="hidden" name="stateType" value="@param.stateType">
-		        <div class="control-group">
-		            <label class="control-label" for="inlineCheckboxes"></label>
-		            <div class="controls inline">
-		            	<fieldset>
-<!--FIXME view 전문가님이 아래의 if-else문 안의 중복된 코드를 업그레이드 해주시길 바랍니다. -->
-                    <input class="filters" type="checkbox" name="commentedCheck" id="commentCheckId" value="1" @if(param.commentedCheck==true){checked="checked"}>
-                    <label class="checkbox inline">@Messages("checkbox.commented")</label>
-		                <input class="filters" type="checkbox" name="fileAttachedCheck" id="fileAttachedCheckId" value="1" @if(param.fileAttachedCheck==true){checked="checked"}>
-                    <label class="checkbox inline">@Messages("checkbox.fileAttached")</label>
-<!--// -->
-		              	<a href="@routes.IssueApp.extractExcelFile(project.owner, project.name, param.stateType)"><i class ="icon-download-alt"></i>@Messages("button.excelDownload")</a>
-		            	</fieldset>
-		            </div>
-		        </div>
-		    </form>
-		</div>
-			<table class="table">
-				<thead>
-					<tr>
-						@header(Messages("label.id"),"id")
-						@header(Messages("label.state"), "state")
-						@header(Messages("label.title"), "title")
-						@header(Messages("label.assignee"), "assigneeId")
-						@header(Messages("label.date"), "date")
-					</tr>
-				</thead>
-				<tbody>
-					@for(issue <- currentPage.getList()){
-						<tr>
-							<td>@issue.id</td>
-							<td>@Messages(issue.state.state)</td>
-							<td><a href="@routes.IssueApp.issue(project.owner, project.name, issue.id)">@issue.title</a></td>
-							<td><a href="@routes.IssueApp.issue(project.owner, project.name, issue.id)"></a></td>
-							<td>
-								@if(issue.assigneeId == null){
-								<em>@Messages("issue.noAssignee")</em>						
-							} else {
-								<em>@issue.assigneeName</em>
-							}
-							</td>
-							<td>@agoString(issue.ago())</td>
+<style>
+@@IMPORT url("/assets/stylesheets/issue.css");
+</style>
+<div class="page-padding">
+  <ul class="breadcrumb page-navi pull-right">
+      <li><a href="#" class="icon-home"></a><span class="divider">&gt;</span></li>
+      <li><a href="#">project home</a><span class="divider">&gt;</span></li>
+      <li class="active">Issue</li>
+  </ul>
 
-						</tr>
-					}
-			</tbody>
-		</table>
+  <ul class="breadcrumb project-name">
+      <li><a href="#">@project.owner</a><span class="divider">/</span></li>
+      <li class="active"><a href="#">@project.name</a></li>
+  </ul>
 
-	    <div class="row">
-            <a class="btn pull-right" href="@routes.IssueApp.enrollAutoNotification(project.owner, project.name)"><i class="icon-ok"></i>@Messages("button.autoNotification")</a>
-            @pagination(currentPage, 5, "pagination")
-        </div>
- 
+  <div class="dashboard">
+    <dl class="row-fluid">
+      <div class="span4"><dt><b>All</b> issues</dt><dd class="all inner-shadow pull-right" title="312,549">@Issue.countIssues(project.id, State.ALL)</dd></div>
+      <div class="span4"><dt><b>Open</b> issues</dt><dd class="open inner-shadow pull-right" title="32,047">@Issue.countIssues(project.id, State.OPEN)</dd></div>
+      <div class="span4"><dt><b>Closed</b> issues</dt><dd class="closed inner-shadow pull-right" title="4,086">@Issue.countIssues(project.id, State.CLOSED)</dd></div>
+    </dl>
 
-	</div>
+    <hr/>
+    <form class="form-search" action="@routes.IssueApp.issues(project.owner, project.name, param.state)" method="GET">
+      <button type="button" id="advanced-search" class="btn btn-small btn-flat" data-toggle="button">Advanced Search</button>
+      <a href="@routes.IssueApp.extractExcelFile(project.owner, project.name, param.state)" class="btn btn-small btn-flat">Download Excel file</a>
+      <div class="input-append pull-right">
+        <input type="hidden" name="orderBy" value="@param.orderBy" class="h-value order">
+        <input type="hidden" name="state" value="@param.state">
+        <input name="filter" class="span2" id="appendedInputButton" size="16" type="text"><button class="btn" type="button"  placeholder="현재 게시글에서 검색">SEARCH</button>
+      </div>
+    </form>
+
+    <form class="form-horizontal" id="labels-form">
+      <!-- issue.label module make this as label filter and editor -->
+      <fieldset class="labels"></fieldset>
+    </form>
+
+  </div>
+
+  <p class="ordering">
+      <span>▲상태순</span>
+      <span>▲날짜순</span>
+      <span selected>▼댓글순</span>
+  </p>
+
+  <table>
+    <tbody>
+
+      @for(issue <- currentPage.getList.reverse){
+        <tr>
+          <td class="no">@issue.id</td>
+          <td class="attachmend attached">@if(Attachment.findByContainer(Resource.ISSUE_POST, issue.id).size > 0){<span class="icon-file"/>}</td>
+          <td class="info">
+            <p><a href="@routes.IssueApp.issue(project.owner, project.name, issue.id)">@issue.title</a></p>
+            <p class="author">by <a href="/users/@issue.authorId">@issue.authorName</a>
+            </a> @agoString(issue.ago())</p>
+          </td>
+          <td class="state @issue.state.toString.toLowerCase">@Messages(issue.state.state)</td>
+          <td class="comments">@issue.numOfComments</td>
+          <td class="assignee"><a href="#"><img class="user-picture" src="/assets/images/default-avatar-34.png"></a>
+              <span>@{ if (issue.assigneeId == null) Messages("issue.noAssignee")
+                else issue.assigneeName }</span>
+          </td>
+
+        </tr>
+      }
+
+    <tbody>
+  </table>
+  <div class="pull-right">
+    <a class="btn btn-primary" href="@routes.IssueApp.newIssue(project.owner, project.name)" >@Messages("issue.menu.new")</a>
+  </div>
+  <div class="pagination pagination-centered">
+    @pagination(currentPage, 5, "pagination")
+  </div>
 </div>
-}
+
 <script type="text/javascript">
+  nforge.require('issue.label', '@routes.IssueLabelApp.getAll(project.owner, project.name)', '@routes.IssueLabelApp.post(project.owner, project.name)', {editable: true});
   nforge.require('issue.list');
 </script>
 }
app/views/issue/newIssue.scala.html
--- app/views/issue/newIssue.scala.html
+++ app/views/issue/newIssue.scala.html
@@ -2,7 +2,7 @@
 
 @import helper._
 @import scala.collection.mutable.Map
-@implicitFieldConstructor = @{ FieldConstructor(twitterBootstrapInput.render) } 
+@implicitFieldConstructor = @{ FieldConstructor(twitterBootstrapInput.render) }
 
 @isVisible(resource: models.enumeration.Resource)(content: => Html) = @{
   roleCheck(session.get("userId"), project.id, resource, models.enumeration.Operation.EDIT){
@@ -11,46 +11,57 @@
 }
 
 @main(Messages(title), project) {
-	<div class="page-header">
-		<h1>@Messages(title)</h1>
-	</div>
-    @form(action = routes.IssueApp.saveIssue(project.owner, project.name), 
-    				'enctype -> "multipart/form-data",
-    				'class -> "form-horizontal") {
-    	<fieldset>
-    	@inputText(
-    		issueForm("title"), 
-    		'_showConstraints -> false, 
+    <style>
+    @@IMPORT url("/assets/stylesheets/issue.css");
+    </style>
+
+    <div class="page-header">
+        <h1>@Messages(title)</h1>
+    </div>
+
+    @form(action = routes.IssueApp.saveIssue(project.owner, project.name),
+                    'id -> "issue-form",
+                    'enctype -> "multipart/form-data",
+                    'class -> "form-horizontal",
+                    'onkeypress -> "return event.keyCode !== 13"
+                    ) {
+        <fieldset>
+        @inputText(
+            issueForm("title"),
+            '_showConstraints -> false,
             '_label-> Messages("post.new.title"),
             'class -> "input-xxlarge")
-    	@textarea(
-    		issueForm("body"), 
-    		'_showConstraints -> false,
+        @textarea(
+            issueForm("body"),
+            '_showConstraints -> false,
             '_label-> Messages("post.new.contents"),
             'rows -> 16,
             'class -> "input-xxlarge textbody",
             'markdown -> true)
-    	</fieldset>
+        </fieldset>
 
-		<fieldset>
+        <!-- issue.label module make this as label selector -->
+        <fieldset class="labels"></fieldset>
+
+        <fieldset>
             <div class="well">
                 <legend><b>@Messages("issue.new.environment")</b></legend>
                 <div class="well form-inline">
                         @selectEx(
-                            issueForm("osType"), 
+                            issueForm("osType"),
                             options(Issue.osTypes),
                             '_label-> Messages("issue.new.environment.osType"),
                             '_default ->  Messages("issue.new.selectDefault.osType"),
                             '_showConstraints -> false)
-                        
+
                         @selectEx(
-                            issueForm("browserType"), 
+                            issueForm("browserType"),
                             options(Issue.browserTypes),
                             '_label-> Messages("issue.new.environment.browserType"),
                             '_default -> Messages("issue.new.selectDefault.browserType"),
                             '_showConstraints -> false)
                         @selectEx(
-                            issueForm("dbmsType"), 
+                            issueForm("dbmsType"),
                             options(Issue.dbmsTypes),
                             '_label-> Messages("issue.new.environment.dbmsType"),
                             '_default -> Messages("issue.new.selectDefault.dbmsType"),
@@ -59,81 +70,41 @@
             </div>
         </fieldset>
 
-		<fieldset>
-			<div class="well">
-	    		<legend><b>@Messages("issue.new.detailInfo")</b></legend>
-	    		<div class="well form-inline">
-                @isVisible(models.enumeration.Resource.ISSUE_CATEGORY) {		  	
-				  	@selectEx(
-				    		issueForm("issueType"), 
-				    		options(Issue.issueTypes),
-				    		'_label-> Messages("issue.new.detailInfo.issueType"),
-				    		'_default -> Messages("issue.new.selectDefault.issueType"),
-				    		'_showConstraints -> false)
-				    	}
-				  @isVisible(models.enumeration.Resource.ISSUE_ASSIGNEE) {
-				    	@select(
-				    		issueForm("assigneeId"), 
-				    		options(ProjectUser.options(project.id)),
-				    		'_label-> Messages("issue.new.detailInfo.assignee"),
-				    		'_default -> Messages("issue.new.selectDefault.assignee"),
-				    		'_showConstraints -> false)
+        <fieldset>
+            <div class="well">
+                <legend><b>@Messages("issue.new.detailInfo")</b></legend>
+                <div class="well form-inline">
+                  @isVisible(models.enumeration.Resource.ISSUE_ASSIGNEE) {
+                        @select(
+                            issueForm("assigneeId"),
+                            options(ProjectUser.options(project.id)),
+                            '_label-> Messages("issue.new.detailInfo.assignee"),
+                            '_default -> Messages("issue.new.selectDefault.assignee"),
+                            '_showConstraints -> false)
                         }
-				  @isVisible(models.enumeration.Resource.ISSUE_COMPONENT) {  	
-				    	@select(
-				    		issueForm("componentName"), 
-				    		options = options(
-				    					"component_1"->"Component id 1"),
-				    		'_label-> Messages("issue.new.detailInfo.component"),
-				    		'_default -> Messages("issue.new.selectDefault.component"),
-				    		'_showConstraints -> false)
-				    	}
-				   @isVisible(models.enumeration.Resource.ISSUE_MILESTONE) {
-				    	@select(
-				    		issueForm("milestoneId"), 
-				    		options(Milestone.options(project.id)),
-				    		'_label-> Messages("issue.new.detailInfo.milestone"),
-				    		'_default -> Messages("issue.new.selectDefault.milestone"),
-				    		'_showConstraints -> false)
-				        }
-				</div>		
-	    	</div>
-	               
-	    </fieldset>
-    	<fieldset>
-    		<div class="well">
-    			<legend><b>@Messages("issue.new.result")</b></legend>
-    			<div class="well form-inline">
-                    @isVisible(models.enumeration.Resource.ISSUE_IMPORTANCE) {				    	
-				    	@selectEx(
-				    		issueForm("importance"), 
-				    		options(Issue.importances),
-				    		'_label-> Messages("issue.new.result.importance"),
-				    		'_default -> Messages("issue.new.selectDefault.importance"),
-				    		'_showConstraints -> false)
-				    	}
-				    @isVisible(models.enumeration.Resource.ISSUE_DIAGNOSISRESULT) {
-				    	@selectEx(
-				    		issueForm("diagnosisResult"), 
-				    		options(Issue.diagnosisResults),
-				    		'_label-> Messages("issue.new.result.diagnosisResult"),
-				    		'_default -> Messages("issue.new.selectDefault.diagnosisResult"),
-				    		'_showConstraints -> false)
-				    	}
-				</div>		
-    		</div>
-    	</fieldset>
+                  @isVisible(models.enumeration.Resource.ISSUE_MILESTONE) {
+                        @select(
+                            issueForm("milestoneId"),
+                            options(Milestone.options(project.id)),
+                            '_label-> Messages("issue.new.detailInfo.milestone"),
+                            '_default -> Messages("issue.new.selectDefault.milestone"),
+                            '_showConstraints -> false)
+                        }
+                </div>
+            </div>
+        </fieldset>
 
-	<div class="row pull-right">
-			<div class="actions">
-				<input type="submit" class="btn btn-primary" value="@Messages("button.save")"> 
-				<a href="@routes.IssueApp.issues(project.owner, project.name, "all")" class="btn">@Messages("button.cancel")</a>
-			</div>
+    <div class="row pull-right">
+            <div class="actions">
+                <input type="submit" class="btn btn-primary" value="@Messages("button.save")">
+                <a href="@routes.IssueApp.issues(project.owner, project.name, "all")" class="btn">@Messages("button.cancel")</a>
+            </div>
     </div>
  }
 
 @views.html.markdown()
 <script type="text/javascript">
   nforge.require('shortcut.submit');
+  nforge.require('issue.label', '@routes.IssueLabelApp.getAll(project.owner, project.name)', '@routes.IssueLabelApp.post(project.owner, project.name)');
 </script>
 }
app/views/layout.scala.html
--- app/views/layout.scala.html
+++ app/views/layout.scala.html
@@ -33,6 +33,8 @@
     <script src="@getJSLink("modules/code")" type="text/javascript"></script>
     <script src="@getJSLink("modules/markdown")" type="text/javascript"></script>
     <script src="@getJSLink("modules/shortcut")" type="text/javascript"></script>
+    <script src="@getJSLink("rgbcolor")" type="text/javascript"></script>
+    <script src="@getJSLink("jquery.form")" type="text/javascript"></script>
 </head>
 
 <body class="@thema">
app/views/milestone/list.scala.html
--- app/views/milestone/list.scala.html
+++ app/views/milestone/list.scala.html
@@ -65,8 +65,8 @@
                     @milestone.contents
                 </td>
                 <td>
-                    <a href="@makeIssuesLink(milestone.id,"closed")">@milestone.numClosedIssues</a> <span>@Messages("issue.stateType.closed")</span>
-                    <a href="@makeIssuesLink(milestone.id,"open")">@milestone.numOpenIssues</a> <span>@Messages("issue.stateType.open")</span>
+                    <a href="@makeIssuesLink(milestone.id,"closed")">@milestone.numClosedIssues</a> <span>@Messages("issue.state.closed")</span>
+                    <a href="@makeIssuesLink(milestone.id,"open")">@milestone.numOpenIssues</a> <span>@Messages("issue.state.open")</span>
                     <div class="progress">
                         <div class="bar" style="width: @milestone.completionRate%;"></div>
                     </div>
@@ -81,4 +81,4 @@
     <div style="text-align: right">
         <a class="btn btn-primary" href="@routes.MilestoneApp.manageMilestones(projectInst.owner, projectInst.name)" >@Messages("milestone.menu.manage")</a>
     </div> 
-}
(No newline at end of file)
+}
conf/initial-data.yml
--- conf/initial-data.yml
+++ conf/initial-data.yml
@@ -98,8 +98,7 @@
         authorId:     2       
         title:          불필요한 로그 출력 코드 제거
         body:           내용 불필요한~
-        state:          ENROLLED
-        stateType:      OPEN
+        state:      OPEN
         milestoneId:    1
         project:        !!models.Project
                             id: 1
@@ -108,8 +107,7 @@
         assigneeId:     2     
         title:          다운로드는 익명 댓글에도 사용자명에 링크가 걸림
         body:           내용 다운로드는 익명 댓글에도 사용자명에 링크가 걸림
-        state:          ASSIGNED
-        stateType:      OPEN
+        state:      OPEN
         milestoneId:    2
         project:        !!models.Project
                             id: 1
@@ -118,10 +116,8 @@
         assigneeId:     2
         title:          gittracker.php의 메모리 제한 에러
         body:           내용 gittracker.php의 메모리 제한 에러
-        state:          SOLVED
-        stateType:      CLOSED
+        state:      CLOSED
         milestoneId:    2
-        diagnosisResult: 2
         project:        !!models.Project
                             id: 1
         numOfComments:  1
@@ -130,10 +126,8 @@
         assigneeId:     3
         title:          git/hg 코드 브라우저에 i18n이 적용되지 않음
         body:           내용 git/hg 코드 브라우저에 i18n이 적용되지 않음
-        state:          FINISHED
-        stateType:      CLOSED
+        state:      CLOSED
         milestoneId:    2
-        diagnosisResult: 5
         project:        !!models.Project
                             id: 1
     - !!models.Issue
@@ -141,8 +135,7 @@
         assigneeId:     3
         title:          CUBRID 설치 문제
         body:           IOS는 설치 못하나요?
-        state:          ASSIGNED
-        stateType:      OPEN
+        state:      OPEN
         milestoneId:    0
         project:        !!models.Project
                             id: 3
@@ -151,10 +144,8 @@
         assigneeId:     3
         title:          메모리 누수 현상
         body:           메모리가 너무 누수가 되는듯.
-        state:          FINISHED
-        stateType:      CLOSED
+        state:      CLOSED
         milestoneId:    6
-        diagnosisResult: 1
         project:        !!models.Project
                             id: 3
     - !!models.Issue
@@ -162,8 +153,7 @@
         assigneeId:    3
         title:          Client application for Mac.
         body:           Please make it.
-        state:          ASSIGNED
-        stateType:      OPEN
+        state:      OPEN
         milestoneId:    5
         project:        !!models.Project
                             id: 3
@@ -172,10 +162,8 @@
         assigneeId:     3
         title:          CPU 무한 점유울 문제.
         body:           CPU를 무한사용 중이에요.
-        state:          FINISHED
-        stateType:      CLOSED
+        state:      CLOSED
         milestoneId:    5
-        diagnosisResult: 4
         project:        !!models.Project
                             id: 3
     - !!models.Issue
@@ -183,10 +171,8 @@
         assigneeId:     3
         title:          Less chained imports causes compile error
         body:           When using Play, when I chain less files such as a.less imports b.less which in turn imports c.less
-        state:          FINISHED
-        stateType:      CLOSED
+        state:      CLOSED
         milestoneId:    1
-        diagnosisResult: 4
         project:        !!models.Project
                             id: 1
     - !!models.Issue
@@ -194,18 +180,15 @@
         assigneeId:     2
         title:          Weird TypeDoesNotMatch exception in RC-3 and final
         body:           The following code works as expected in RC1-Snapshot, but breaks with an TypeDoesNotMatch exception in RC-3 and 2.0 final.
-        state:          FINISHED
-        stateType:      CLOSED
+        state:      CLOSED
         milestoneId:    1
-        diagnosisResult: 3
         project:        !!models.Project
                             id: 1
     - !!models.Issue
         authorId:     1
         title:          Anorm and PostgreSQL - return value not accessible
         body:           PostgreSQL allows you to insert a row and obtain the id of the new row in one query
-        state:          ENROLLED
-        stateType:      OPEN
+        state:      OPEN
         milestoneId:    1
         project:        !!models.Project
                             id: 1
@@ -214,10 +197,8 @@
         assigneeId:     3
         title:          Update sbt-idea to 1.1.0
         body:           Create sbt project definition module, if exists, for each subproject (pull 128)
-        state:          FINISHED
-        stateType:      CLOSED
+        state:      CLOSED
         milestoneId:    3
-        diagnosisResult: 3
         project:        !!models.Project
                             id: 2
     - !!models.Issue
@@ -225,10 +206,8 @@
         assigneeId:    3
         title:          Support Tuple 22, not just Tuple 18 in api/data/Forms.scala
         body:           While creating some complex forms, Eclipse started hanging for some unknown reason.
-        state:          FINISHED
-        stateType:      CLOSED
+        state:      CLOSED
         milestoneId:    3
-        diagnosisResult: 3
         project:        !!models.Project
                             id: 2
     - !!models.Issue
@@ -236,8 +215,7 @@
         assigneeId:     3
         title:          Support Tuple 22, not just Tuple 18 in api/data/Forms.scala
         body:           While creating some complex forms, Eclipse started hanging for some unknown reason.
-        state:          ASSIGNED
-        stateType:      OPEN
+        state:      OPEN
         milestoneId:    4
         project:        !!models.Project
                             id: 2
@@ -246,8 +224,7 @@
         assigneeId:     3
         title:          Cookie Expires date
         body:           With Play 2.0.2, when creating a cookie with response().setCookie, HTTP output is like
-        state:          ASSIGNED
-        stateType:      OPEN
+        state:      OPEN
         milestoneId:    4
         project:        !!models.Project
                             id: 2
@@ -256,8 +233,7 @@
         assigneeId:     3
         title:          require is not work in windows (Closure Compiler)
         body:           Test on Play2.1-Snapshot
-        state:          ASSIGNED
-        stateType:      OPEN
+        state:      OPEN
         milestoneId:    4
         project:        !!models.Project
                             id: 2
@@ -266,8 +242,7 @@
         assigneeId:     3
         title:          Remember me is not working
         body:           Test on Play2.1-Snapshot
-        state:          ASSIGNED
-        stateType:      OPEN
+        state:      OPEN
         milestoneId:    4
         project:        !!models.Project
                             id: 3
@@ -363,12 +338,6 @@
             - !!models.Permission
                 id: 8 
             - !!models.Permission
-                id: 9 
-            - !!models.Permission
-                id: 10 
-            - !!models.Permission
-                id: 11  
-            - !!models.Permission
                 id: 12 
             - !!models.Permission
                 id: 13 
@@ -381,12 +350,6 @@
             - !!models.Permission
                 id: 17  
             - !!models.Permission
-                id: 18
-            - !!models.Permission
-                id: 19
-            - !!models.Permission
-                id: 20
-            - !!models.Permission
                 id: 21  
             - !!models.Permission
                 id: 22 
@@ -398,18 +361,6 @@
                 id: 25 
             - !!models.Permission
                 id: 26 
-            - !!models.Permission
-                id: 27  
-            - !!models.Permission
-                id: 28 
-            - !!models.Permission
-                id: 29 
-            - !!models.Permission
-                id: 30 
-            - !!models.Permission
-                id: 31  
-            - !!models.Permission
-                id: 32 
             - !!models.Permission
                 id: 33 
             - !!models.Permission
@@ -485,10 +436,6 @@
             - !!models.Permission
                 id: 6 
             - !!models.Permission
-                id: 9 
-            - !!models.Permission
-                id: 10 
-            - !!models.Permission
                 id: 12 
             - !!models.Permission
                 id: 13 
@@ -501,12 +448,6 @@
             - !!models.Permission
                 id: 17  
             - !!models.Permission
-                id: 18
-            - !!models.Permission
-                id: 19
-            - !!models.Permission
-                id: 20
-            - !!models.Permission
                 id: 21  
             - !!models.Permission
                 id: 22 
@@ -518,18 +459,6 @@
                 id: 25 
             - !!models.Permission
                 id: 26 
-            - !!models.Permission
-                id: 27  
-            - !!models.Permission
-                id: 28 
-            - !!models.Permission
-                id: 29 
-            - !!models.Permission
-                id: 30 
-            - !!models.Permission
-                id: 31  
-            - !!models.Permission
-                id: 32 
             - !!models.Permission
                 id: 33 
             - !!models.Permission
@@ -583,12 +512,6 @@
             - !!models.Permission
                 id: 8 
             - !!models.Permission
-                id: 9 
-            - !!models.Permission
-                id: 10 
-            - !!models.Permission
-                id: 11  
-            - !!models.Permission
                 id: 12 
             - !!models.Permission
                 id: 13 
@@ -601,12 +524,6 @@
             - !!models.Permission
                 id: 17  
             - !!models.Permission
-                id: 18
-            - !!models.Permission
-                id: 19
-            - !!models.Permission
-                id: 20
-            - !!models.Permission
                 id: 21  
             - !!models.Permission
                 id: 22 
@@ -618,18 +535,6 @@
                 id: 25 
             - !!models.Permission
                 id: 26 
-            - !!models.Permission
-                id: 27  
-            - !!models.Permission
-                id: 28 
-            - !!models.Permission
-                id: 29 
-            - !!models.Permission
-                id: 30 
-            - !!models.Permission
-                id: 31  
-            - !!models.Permission
-                id: 32 
             - !!models.Permission
                 id: 33 
             - !!models.Permission
@@ -707,23 +612,13 @@
             - !!models.Permission
                 id: 6 
             - !!models.Permission
-                id: 9 
-            - !!models.Permission
-                id: 10 
-            - !!models.Permission
                 id: 12 
             - !!models.Permission
                 id: 15 
             - !!models.Permission
-                id: 18
-            - !!models.Permission
                 id: 21  
             - !!models.Permission
                 id: 24  
-            - !!models.Permission
-                id: 27  
-            - !!models.Permission
-                id: 30 
             - !!models.Permission
                 id: 33 
             - !!models.Permission
@@ -782,19 +677,6 @@
         operation:      delete
 
     - !!models.Permission
-        id:             9
-        resource:       issue_environment
-        operation:      read
-    - !!models.Permission
-        id:             10
-        resource:       issue_environment
-        operation:      write
-    - !!models.Permission
-        id:             11
-        resource:       issue_environment
-        operation:      edit
-
-    - !!models.Permission
         id:             12
         resource:       issue_assignee
         operation:      read
@@ -821,19 +703,6 @@
         operation:      edit
 
     - !!models.Permission
-        id:             18
-        resource:       issue_importance
-        operation:      read
-    - !!models.Permission
-        id:             19
-        resource:       issue_importance
-        operation:      write
-    - !!models.Permission
-        id:             20
-        resource:       issue_importance
-        operation:      edit
-
-    - !!models.Permission
         id:             21
         resource:       issue_category
         operation:      read
@@ -857,32 +726,6 @@
     - !!models.Permission
         id:             26
         resource:       issue_milestone
-        operation:      edit
-
-    - !!models.Permission
-        id:             27
-        resource:       issue_component
-        operation:      read
-    - !!models.Permission
-        id:             28
-        resource:       issue_component
-        operation:      write
-    - !!models.Permission
-        id:             29
-        resource:       issue_component
-        operation:      edit
-
-    - !!models.Permission
-        id:             30
-        resource:       issue_diagnosisResult
-        operation:      read
-    - !!models.Permission
-        id:             31
-        resource:       issue_diagnosisResult
-        operation:      write
-    - !!models.Permission
-        id:             32
-        resource:       issue_diagnosisResult
         operation:      edit
 
     - !!models.Permission
@@ -1022,19 +865,6 @@
         resource:       site_setting
         operation:      write
         
-    - !!models.Permission
-        id:             65
-        resource:       issue_importance
-        operation:      read
-    - !!models.Permission
-        id:             66
-        resource:       issue_importance
-        operation:      write
-    - !!models.Permission
-        id:             67
-        resource:       issue_importance
-        operation:      edit
-
 # ProjectUser
 projectUsers:
     - !!models.ProjectUser
@@ -1197,6 +1027,3 @@
         body:           test item2
         checklist :     !!models.task.Checklist
                             id:     1
-    
-                
-                
(No newline at end of file)
conf/messages.ko
--- conf/messages.ko
+++ conf/messages.ko
@@ -77,13 +77,9 @@
 sort.by.completionRate = 완료울순
 
 #Issue
-issue.stateType.all = 전체
-issue.stateType.open = 미해결
-issue.stateType.closed = 해결
-issue.state.assigned = 진행중
-issue.state.enrolled = 등록
-issue.state.solved = 해결
-issue.state.finished = 닫힘
+issue.state.all = 전체
+issue.state.open = 미해결
+issue.state.closed = 해결
 issue.is.empty = 등록된 이슈가 없습니다. 
 issue.menu.searchDefault = 이슈 검색
 issue.menu.milestoneSelectDefault = 마일스톤 선택
conf/routes
--- conf/routes
+++ conf/routes
@@ -70,9 +70,9 @@
 GET     /:userName/:projectName/milestone/:id/edit      controllers.MilestoneApp.editMilestone(userName: String, projectName:String, id: Long)
 
 # Issues
-GET     /:userName/:projectName/issueList               controllers.IssueApp.issues(userName:String, projectName:String, stateType:String)
+GET     /:userName/:projectName/issueList               controllers.IssueApp.issues(userName:String, projectName:String, state:String)
 GET     /:userName/:projectName/issueList/autonotify    controllers.IssueApp.enrollAutoNotification(userName:String, projectName:String)
-GET     /:userName/:projectName/issueList/excel         controllers.IssueApp.extractExcelFile(userName:String, projectName:String, stateType:String)
+GET     /:userName/:projectName/issueList/excel         controllers.IssueApp.extractExcelFile(userName:String, projectName:String, state:String)
 GET     /:userName/:projectName/issues/new              controllers.IssueApp.newIssue(userName:String, projectName:String)
 POST    /:userName/:projectName/issues/new              controllers.IssueApp.saveIssue(userName:String, projectName:String)
 GET     /:userName/:projectName/issues/:id              controllers.IssueApp.issue(userName:String, projectName:String, id:Long)
@@ -83,6 +83,10 @@
 POST    /:userName/:projectName/issues/:id/edit         controllers.IssueApp.updateIssue(userName:String, projectName:String, id:Long)
 GET		/issuedetail									controllers.IssueApp.getIssueDatil()
 
+# Label
+POST	/:userName/:projectName/labels					controllers.IssueLabelApp.post(userName:String, projectName:String)
+GET		/:userName/:projectName/labels					controllers.IssueLabelApp.getAll(userName:String, projectName:String)
+POST	/:userName/:projectName/labels/:id		  	    controllers.IssueLabelApp.delete(userName:String, projectName:String, id:Long)
 
 # Git
 GET     /:ownerName/:projectName/info/refs                                        controllers.GitApp.advertise(ownerName:String, projectName:String)
public/javascripts/modules/issue.js
--- public/javascripts/modules/issue.js
+++ public/javascripts/modules/issue.js
@@ -1,53 +1,299 @@
 nforge.namespace('issue');
-nforge.issue.list = function () {
-  var that,
-    $checkboxForm,
-    $searchForm,
-    $pagination;
+
+// Compute a color contrasted with the given color.
+// e.g.) dimgray if yellow is given.
+var contrasted_color = function(color) {
+  var rgb = new RGBColor(color);
+  // Compute lightness. See http://en.wikipedia.org/wiki/Luma_(video)
+  var y709 = rgb.r * 0.21 + 0.72 * rgb.g + rgb.b * 0.07;
+  return y709 > 192 ? 'dimgray' : 'white';
+};
+
+nforge.issue.label = function () {
+  var that;
+
   that = {
-    init : function () {
-      $checkboxForm = $("#checkboxForm");
-      $searchForm = $("#searchForm");
-      $pagination = $("#pagination");
-      that.setEvent();
-    },
-
-    setEvent : function () {
-      $checkboxForm.find('.filters').click(that.filter);
-      $('.th-sort').click(that.search);
-
-      $("#milestone").change(function () {
-        $searchForm.submit();
-      });
-
-      /* TODO */
-      $pagination.click(function () {
-        console.log('TBD..');
-      });
-    },
-
-    filter : function (e) {
-      setTimeout(function () {
-        $checkboxForm.submit();
-      }, 1);
-    },
-
-    search : function (e) {
-      nforge.stopEvent(e);
-      var _self = $(this),
-        sortBy = _self.data('sortBy'),
-        $hiddenInputs = $searchForm.find('h-value'),
-        $sortInput = $hiddenInputs.filter('.sort'),
-        $orderInput = $hiddenInputs.filter('.order'),
-        orderVal = $orderInput.val();
-
-      if ($sortInput.val() !== sortBy) {
-        $sortInput.val(sortBy);
-      } else {
-        $orderInput.val(orderVal === 'asc' ? 'desc' : orderVal);
+    init: function(urlToLabels, urlToPost, options) {
+      that.urlToLabels = urlToLabels;
+      that.urlToPost = urlToPost;
+      that.options = {
+        editable: false
+      };
+      for(var key in options) {
+        that.options[key] = options[key];
       }
-      $searchForm.submit();
+
+      if (that.options.editable) {
+        that.addLabelEditor();
+      }
+      that.setEvent();
+      that.updateLabels();
+    },
+
+    addLabelEditor: function() {
+      var div = $('<div>')
+        .addClass('control-group');
+
+      var label = $('<label id="custom-label-label">')
+        .addClass('control-label')
+        .text('New Label');
+
+      var controls = $('<div id="custom-label">')
+        .addClass('controls');
+
+      /*
+      colors = ['gray', 'red', 'orange', 'yellow', 'green',
+        'CornflowerBlue', 'blue', 'purple', 'white'];
+      */
+
+      colors = ['#999999', '#da5454', '#ff9933', '#ffcc33', '#99ca3c',
+        '#22b4b9', '#4d68b1', '#9966cc', '#ffffff'];
+
+      for (var i = 0; i < colors.length; i++) {
+        var button = $('<button type="button">')
+          .addClass('issue-label')
+          .css('background-color', colors[i]);
+        controls.append(button);
+      }
+
+      var input_color = $('<input id="custom-label-color" type="text">')
+        .addClass('input-medium')
+        .attr('placeholder', 'Custom Color');
+
+      var input_category = $('<input id="custom-label-category" type="text">')
+        .addClass('input-small')
+        .attr('data-provider', 'typeahead')
+        .attr('placeholder', 'Category');
+
+      var input_name = $('<input id="custom-label-name" type="text">')
+        .addClass('input-small')
+        .attr('placeholder', 'Name');
+
+      var input_submit = $('<button id="custom-label-submit" type="button">')
+        .addClass('btn')
+        .text('Add');
+
+      controls.append(input_color);
+      controls.append(input_category);
+      controls.append(input_name);
+      controls.append(input_submit);
+
+      div.append(label);
+      div.append(controls);
+      $('fieldset.labels').append(div);
+    },
+
+    updateLabels: function() {
+      var form = $('<form>')
+        .attr('method', 'get')
+        .attr('action', that.urlToLabels);
+
+      form.ajaxForm({
+        success: function(responseBody, statusText, xhr) {
+          var labels = responseBody;
+
+          if (!(labels instanceof Object)) {
+            alert('Failed to update - Server error.');
+            return;
+          }
+
+          for (var i = 0; i < labels.length; i++) {
+            that.add_label_into_category(labels[i].id, labels[i].category, labels[i].name, labels[i].color);
+          }
+        }
+      });
+
+      form.submit();
+    },
+
+    setEvent: function() {
+      $('#custom-label button.issue-label').click(function(e) {
+        $('#custom-label button.issue-label').removeClass('active');
+        $(e.srcElement).addClass('active');
+        $('#custom-label-color').val($(e.srcElement).css('background-color'));
+        $('#custom-label-category').focus();
+        $('#custom-label-name').css('background-color', $(e.srcElement).css('background-color'));
+        var contrasted = contrasted_color($(e.srcElement).css('background-color'));
+        $('#custom-label-name').css('color', contrasted);
+        try {
+          document.styleSheets[0].addRule('#custom-label-name:-moz-placeholder', 'color: ' + contrasted);
+          document.styleSheets[0].addRule('#custom-label-name:-ms-input-placeholder', 'color: ' + contrasted);
+        } catch (e) {
+          document.styleSheets[0].addRule('#custom-label-name::-webkit-input-placeholder', 'color: ' + contrasted);