김지한 2014-02-17
Merge branch `yobi-868` of laziel/yobi
from pull request 583
@17e63c10c359c19713f948686c23cd8881f76416
app/controllers/IssueLabelApp.java
--- app/controllers/IssueLabelApp.java
+++ app/controllers/IssueLabelApp.java
@@ -149,4 +149,36 @@
         label.delete();
         return ok();
     }
+
+    /**
+     * 특정 프로젝트의 모든 이슈라벨의 CSS 스타일을 달라는 요청에 응답한다
+     *
+     * when: 이슈 라벨을 사용하는 페이지에서 라벨 색상(스타일)을 필요로 할 때
+     *
+     * 주어진 {@code ownerName}과 {@code projectName}에 대응되는 프로젝트에 대해,
+     * 그 프로젝트에 속한 이슈라벨의 스타일을 {@code text/css} 형식으로 응답한다.
+     *
+     * 사용자에게 프로젝트에 접근할 권한이 없는 경우에는 {@code 403 Forbidden}으로 응답한다.
+     *
+     * @param ownerName   프로젝트 소유자의 이름
+     * @param projectName 프로젝트의 이름
+     * @return 이슈라벨의 스타일을 달라는 요청에 대한 응답
+     */
+    @IsAllowed(Operation.READ)
+    public static Result labelStyles(String ownerName, String projectName) {
+        Project project = ProjectApp.getProject(ownerName, projectName);
+        List<IssueLabel> labels = IssueLabel.findByProject(project);
+
+        String eTag = "\"" + labels.hashCode() + "\"";
+        String ifNoneMatchValue = request().getHeader("If-None-Match");
+
+        if(ifNoneMatchValue != null && ifNoneMatchValue.equals(eTag)) {
+            response().setHeader("ETag", eTag);
+            return status(NOT_MODIFIED);
+        }
+
+        response().setHeader("ETag", eTag);
+
+        return ok(views.html.common.issueLabelColor.render(labels)).as("text/css");
+    }
 }
 
app/views/common/issueLabelColor.scala.html (added)
+++ app/views/common/issueLabelColor.scala.html
@@ -0,0 +1,76 @@
+@**
+* Yobi, Project Hosting SW
+*
+* Copyright 2014 NAVER Corp.
+* http://yobi.io
+*
+* @Author Jihan Kim
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+**@
+@(labels:List[models.IssueLabel])
+
+@getLabelTextColorFromBgColor(bgColor: String) = @{
+    def parseFromRGB(bgColor:String):Map[String,Int] = {
+        val start = bgColor.indexOf('(') + 1
+        val end = bgColor.indexOf(')')
+        val baseColor = bgColor.substring(start, end).split(",")
+
+        return scala.collection.Map(
+            "R" -> Integer.parseInt(baseColor(0), 10),
+            "G" -> Integer.parseInt(baseColor(1), 10),
+            "B" -> Integer.parseInt(baseColor(2), 10)
+        )
+    }
+
+    def parseFromHex(bgColor:String):Map[String,Int] = {
+        val paramColor: String = if ((bgColor indexOf "#") != 0) "#" + bgColor else bgColor
+        val baseColor: String = if ((paramColor.length == 4)) "#" +
+                                    (paramColor.substring(1) * 2) +
+                                    (paramColor.substring(2) * 2) +
+                                    (paramColor.substring(3) * 2)
+                                else paramColor
+
+        return scala.collection.Map(
+            "R" -> Integer.parseInt(baseColor.substring(1, 3), 16),
+            "G" -> Integer.parseInt(baseColor.substring(3, 5), 16),
+            "B" -> Integer.parseInt(baseColor.substring(5, 7), 16)
+        )
+    }
+
+    def getDefaultRGB():Map[String,Int] = {
+        return scala.collection.Map("R" -> 255, "G" -> 255, "B" -> 255)
+    }
+
+    val YCC_DARK = 192
+
+    val RGB = bgColor.toLowerCase() match {
+        case s if s.startsWith("rgb") => parseFromRGB(s)
+        case s if s.matches("^[#]*[0-9a-z]+$") => parseFromHex(s)
+        case _ => getDefaultRGB()
+    }
+
+    val colorSpace = (RGB.get("R") * 0.21) + (RGB.get("G") * 0.72) + (RGB.get("B") * 0.07)
+
+    if ((colorSpace > YCC_DARK)) "dimgray" else "white"
+}
+
+@for(label <- labels){
+.issue-label[data-labelId="@label.id"]{
+    border-left: 3px solid @label.color;
+}
+.issue-label.active[data-labelId="@label.id"]{
+    background-color: @label.color;
+    color: @getLabelTextColorFromBgColor(label.color);
+}
+}
app/views/milestone/view.scala.html
--- app/views/milestone/view.scala.html
+++ app/views/milestone/view.scala.html
@@ -119,6 +119,7 @@
 
 @common.markdown(project)
 
+<link rel="stylesheet" href="@routes.IssueLabelApp.labelStyles(project.owner, project.name)" type="text/css" />
 <script type="text/javascript">
     $(document).ready(function(){
         $yobi.loadModule("milestone.View");
conf/routes
--- conf/routes
+++ conf/routes
@@ -145,6 +145,7 @@
 GET            /:user/:project/issue/labels                                           controllers.IssueLabelApp.labels(user, project)
 POST           /:user/:project/issue/labels                                           controllers.IssueLabelApp.newLabel(user, project)
 POST           /:user/:project/issue/label/:id/delete                                 controllers.IssueLabelApp.delete(user, project, id:Long)
+GET            /:user/:project/issue/labels.css                                       controllers.IssueLabelApp.labelStyles(user, project)
 
 # Git
 GET            /:ownerName/:project/info/refs                                         controllers.GitApp.advertise(ownerName:String, project:String, service:String ?= null)
Add a comment
List