
Issue: improve layout
- Disable label form and show "Manage Label" text on search form at issue list if no label exists. - issue.View.js has rewritted with new coding convention. - Separate pjax-filter links into partial template file. - Set issue-info wrap affixed
@60ebf00981d83b286f5c4ab422b2f53e7491f8b8
--- app/assets/stylesheets/less/_override.less
+++ app/assets/stylesheets/less/_override.less
... | ... | @@ -10,6 +10,7 @@ |
10 | 10 |
.select2-container { |
11 | 11 |
.avatar-wrap { margin-top:0; } |
12 | 12 |
|
13 |
+ text-align:left; |
|
13 | 14 |
background-color:#fff; |
14 | 15 |
border:1px solid rgba(0,0,0,0.15); |
15 | 16 |
|
... | ... | @@ -57,6 +58,10 @@ |
57 | 58 |
margin-left: 0; |
58 | 59 |
} |
59 | 60 |
} |
61 |
+ |
|
62 |
+ abbr { |
|
63 |
+ top:7px; |
|
64 |
+ } |
|
60 | 65 |
} |
61 | 66 |
|
62 | 67 |
&.select2-drop-above { |
... | ... | @@ -95,7 +100,7 @@ |
95 | 100 |
} |
96 | 101 |
|
97 | 102 |
.select2-results { |
98 |
- max-height: 400px; |
|
103 |
+ max-height: 350px; |
|
99 | 104 |
padding:4px; margin:4px 0 0 0; |
100 | 105 |
|
101 | 106 |
li { margin-bottom:1px; } |
... | ... | @@ -212,12 +217,22 @@ |
212 | 217 |
|
213 | 218 |
.select2-search-field input { |
214 | 219 |
font-family:@base-font-family !important; |
220 |
+ padding:3px 5px; |
|
215 | 221 |
} |
216 | 222 |
} |
217 | 223 |
|
218 | 224 |
&.issue-labels { |
219 | 225 |
border:none; |
220 | 226 |
.box-shadow(none); |
227 |
+ |
|
228 |
+ &.bordered { |
|
229 |
+ border:1px solid #ddd; |
|
230 |
+ |
|
231 |
+ &:hover, |
|
232 |
+ &.select2-container-active { |
|
233 |
+ border:1px solid #ddd; |
|
234 |
+ } |
|
235 |
+ } |
|
221 | 236 |
|
222 | 237 |
.select2-choices { |
223 | 238 |
.select2-search-choice { |
... | ... | @@ -264,6 +279,12 @@ |
264 | 279 |
.box-shadow(none); |
265 | 280 |
} |
266 | 281 |
} |
282 |
+ |
|
283 |
+ &.select2-container-disabled { |
|
284 |
+ .select2-choices { |
|
285 |
+ border:none; |
|
286 |
+ } |
|
287 |
+ } |
|
267 | 288 |
} |
268 | 289 |
.select2-drop.issue-labels { |
269 | 290 |
min-width:240px; |
... | ... | @@ -273,6 +294,15 @@ |
273 | 294 |
|
274 | 295 |
.select2-results { |
275 | 296 |
min-width: 220px; |
297 |
+ } |
|
298 |
+ .select2-result-with-children > .select2-result-label { |
|
299 |
+ padding: 10px 7px; |
|
300 |
+ } |
|
301 |
+ .select2-result-with-children > .select2-result-label:first-of-type { |
|
302 |
+ padding-top: 0; |
|
303 |
+ } |
|
304 |
+ .select2-result-with-children > .select2-result-sub > li:last-of-type { |
|
305 |
+ margin-bottom: 10px; |
|
276 | 306 |
} |
277 | 307 |
} |
278 | 308 |
li.select2-result-with-children:first-of-type { |
... | ... | @@ -288,12 +318,16 @@ |
288 | 318 |
display: none; |
289 | 319 |
} |
290 | 320 |
.select2-drop.branches { |
291 |
- width:auto !important; |
|
292 |
- border-top:1px solid rgba(0,0,0,0.15); |
|
293 |
- .box-shadow(2px 2px 0 rgba(0,0,0,0.05)); |
|
321 |
+ width: auto !important; |
|
322 |
+ border-top: 1px solid rgba(0, 0, 0, 0.15); |
|
323 |
+ .box-shadow(2px 2px 0 rgba(0, 0, 0, 0.05)); |
|
294 | 324 |
.border-radius(3px); |
295 | 325 |
|
296 | 326 |
.select2-results { |
297 | 327 |
width: auto; |
298 | 328 |
} |
299 | 329 |
} |
330 |
+.select2-result-label .issue-label { |
|
331 |
+ display: block; |
|
332 |
+ margin-right: 15px; |
|
333 |
+} |
--- app/assets/stylesheets/less/_page.less
+++ app/assets/stylesheets/less/_page.less
... | ... | @@ -2595,41 +2595,26 @@ |
2595 | 2595 |
|
2596 | 2596 |
.board-body { |
2597 | 2597 |
.author-info { |
2598 |
- padding:15px 0 0 20px; |
|
2599 |
- |
|
2600 |
- dl { margin-bottom:20px; } |
|
2601 |
- dl.author { |
|
2602 |
- height:51px; overflow:hidden; |
|
2603 |
- dd { padding:5px 10px; } |
|
2604 |
- } |
|
2605 |
- p { |
|
2606 |
- margin-bottom: 5px; |
|
2607 |
- line-height: 14px; |
|
2608 |
- } |
|
2609 |
- .name { |
|
2610 |
- font-size: 11px; |
|
2611 |
- } |
|
2612 |
- .status { |
|
2613 |
- margin: 0; |
|
2614 |
- color: #646464; |
|
2615 |
- font-size: 11px; |
|
2616 |
- .ico { |
|
2617 |
- vertical-align: top; |
|
2618 |
- } |
|
2619 |
- } |
|
2598 |
+ display:block; |
|
2599 |
+ margin:10px 20px; |
|
2620 | 2600 |
} |
2601 |
+ |
|
2621 | 2602 |
.content { |
2622 |
- padding:0 20px; |
|
2623 |
- margin-bottom: 20px; |
|
2624 |
- a { font-weight:bold; } |
|
2603 |
+ padding:0 20px; |
|
2604 |
+ margin-bottom: 20px; |
|
2605 |
+ min-height: 250px; |
|
2606 |
+ |
|
2607 |
+ a { font-weight:bold; } |
|
2625 | 2608 |
|
2626 | 2609 |
&.empty-content { |
2627 | 2610 |
text-align: center; |
2628 |
- min-height: 250px; |
|
2629 | 2611 |
background-image:url("@{base-image-path}/no_contents.jpg"); |
2630 | 2612 |
background-repeat: no-repeat; |
2631 | 2613 |
background-position: center 50%; |
2632 |
- } |
|
2614 |
+ } |
|
2615 |
+ } |
|
2616 |
+ textarea.content { |
|
2617 |
+ padding:6px; |
|
2633 | 2618 |
} |
2634 | 2619 |
} |
2635 | 2620 |
.attachments { |
... | ... | @@ -3040,6 +3025,37 @@ |
3040 | 3025 |
position:relative; |
3041 | 3026 |
|
3042 | 3027 |
select { visibility: hidden; } |
3028 |
+} |
|
3029 |
+ |
|
3030 |
+.issue-info { |
|
3031 |
+ padding:15px 0 0 52px; |
|
3032 |
+ &.affix { top: 0; } |
|
3033 |
+ |
|
3034 |
+ dl { |
|
3035 |
+ margin-bottom:20px; |
|
3036 |
+ |
|
3037 |
+ dd { |
|
3038 |
+ padding:5px 0px; |
|
3039 |
+ } |
|
3040 |
+ } |
|
3041 |
+ |
|
3042 |
+ p { |
|
3043 |
+ margin-bottom: 5px; |
|
3044 |
+ line-height: 14px; |
|
3045 |
+ } |
|
3046 |
+ |
|
3047 |
+ .name { |
|
3048 |
+ font-size: 11px; |
|
3049 |
+ } |
|
3050 |
+ |
|
3051 |
+ .status { |
|
3052 |
+ margin: 0; |
|
3053 |
+ color: #646464; |
|
3054 |
+ font-size: 11px; |
|
3055 |
+ .ico { |
|
3056 |
+ vertical-align: top; |
|
3057 |
+ } |
|
3058 |
+ } |
|
3043 | 3059 |
} |
3044 | 3060 |
|
3045 | 3061 |
// -- new attach files // |
... | ... | @@ -5711,7 +5727,7 @@ |
5711 | 5727 |
|
5712 | 5728 |
.label-editor-wrap { |
5713 | 5729 |
.new-label-wrap { |
5714 |
- margin:40px auto 30px; |
|
5730 |
+ margin:30px auto; |
|
5715 | 5731 |
|
5716 | 5732 |
.form-legend { |
5717 | 5733 |
display:block; |
... | ... | @@ -5752,15 +5768,6 @@ |
5752 | 5768 |
.list-item { |
5753 | 5769 |
border-top:1px solid #ddd; |
5754 | 5770 |
|
5755 |
- .category-exclusive { |
|
5756 |
- margin-right:5px; |
|
5757 |
- vertical-align: middle; |
|
5758 |
- cursor:help; |
|
5759 |
- |
|
5760 |
- &.single { color:@yobi-cyan; } |
|
5761 |
- &.multiple { color:@yobi-yello-dark; } |
|
5762 |
- } |
|
5763 |
- |
|
5764 | 5771 |
.category-name { |
5765 | 5772 |
margin-right:2px; |
5766 | 5773 |
} |
... | ... | @@ -5794,6 +5801,15 @@ |
5794 | 5801 |
} |
5795 | 5802 |
} |
5796 | 5803 |
|
5804 |
+.category-exclusive { |
|
5805 |
+ margin-right:5px; |
|
5806 |
+ vertical-align: middle; |
|
5807 |
+ cursor:help; |
|
5808 |
+ |
|
5809 |
+ &.single { color:@yobi-cyan; } |
|
5810 |
+ &.multiple { color:@yobi-yello-dark; } |
|
5811 |
+} |
|
5812 |
+ |
|
5797 | 5813 |
.edit-label-form { |
5798 | 5814 |
margin-top:20px; |
5799 | 5815 |
|
--- app/assets/stylesheets/less/_reponsive.less
+++ app/assets/stylesheets/less/_reponsive.less
... | ... | @@ -119,4 +119,12 @@ |
119 | 119 |
width:980px; |
120 | 120 |
} |
121 | 121 |
} |
122 |
+ |
|
123 |
+ .issue-info { |
|
124 |
+ padding:15px 0 0 10px; |
|
125 |
+ } |
|
126 |
+ |
|
127 |
+ .markdown-help .markdown-help-nav li { |
|
128 |
+ padding: 5px 8px; |
|
129 |
+ } |
|
122 | 130 |
} |
--- app/assets/stylesheets/less/_yobiUI.less
+++ app/assets/stylesheets/less/_yobiUI.less
... | ... | @@ -752,6 +752,7 @@ |
752 | 752 |
&.ybtn-fullsize { |
753 | 753 |
display:block; |
754 | 754 |
width:100%; |
755 |
+ box-sizing:border-box; |
|
755 | 756 |
} |
756 | 757 |
|
757 | 758 |
&.ybtn-padding { |
--- app/controllers/IssueApp.java
+++ app/controllers/IssueApp.java
... | ... | @@ -51,6 +51,7 @@ |
51 | 51 |
import java.util.HashSet; |
52 | 52 |
import java.util.List; |
53 | 53 |
import java.util.Map; |
54 |
+import java.util.Collections; |
|
54 | 55 |
import utils.HttpUtil; |
55 | 56 |
|
56 | 57 |
public class IssueApp extends AbstractPostingApp { |
... | ... | @@ -106,6 +107,7 @@ |
106 | 107 |
models.support.SearchCondition searchCondition = issueParamForm.bindFromRequest().get(); |
107 | 108 |
searchCondition.pageNum = pageNum - 1; |
108 | 109 |
searchCondition.labelIds.addAll(LabelSearchUtil.getLabelIds(request())); |
110 |
+ searchCondition.labelIds.remove(null); |
|
109 | 111 |
|
110 | 112 |
// determine pjax or json when requested with XHR |
111 | 113 |
if (HttpUtil.isRequestedWithXHR(request())) { |
... | ... | @@ -664,7 +666,9 @@ |
664 | 666 |
String[] labelIds = form.get("labelIds"); |
665 | 667 |
if (labelIds != null) { |
666 | 668 |
for (String labelId : labelIds) { |
667 |
- issue.labels.add(IssueLabel.finder.byId(Long.parseLong(labelId))); |
|
669 |
+ if(!StringUtils.isEmpty(labelId)) { |
|
670 |
+ issue.labels.add(IssueLabel.finder.byId(Long.parseLong(labelId))); |
|
671 |
+ } |
|
668 | 672 |
} |
669 | 673 |
} |
670 | 674 |
} |
--- app/controllers/IssueLabelApp.java
+++ app/controllers/IssueLabelApp.java
... | ... | @@ -114,14 +114,14 @@ |
114 | 114 |
Project project = Project.findByOwnerAndProjectName(ownerName, projectName); |
115 | 115 |
List<IssueLabel> labels = IssueLabel.findByProject(project); |
116 | 116 |
|
117 |
- return ok(views.html.issue.partial_labels_list.render(project, labels)); |
|
117 |
+ return ok(views.html.project.partial_issuelabels_list.render(project, labels)); |
|
118 | 118 |
} |
119 | 119 |
|
120 | 120 |
private static Result labelsAsHTML(String ownerName, String projectName){ |
121 | 121 |
Project project = Project.findByOwnerAndProjectName(ownerName, projectName); |
122 | 122 |
List<IssueLabel> labels = IssueLabel.findByProject(project); |
123 | 123 |
|
124 |
- return ok(views.html.issue.labels.render(project, labels)); |
|
124 |
+ return ok(views.html.project.issuelabels.render(project, labels)); |
|
125 | 125 |
} |
126 | 126 |
|
127 | 127 |
/** |
--- app/models/Issue.java
+++ app/models/Issue.java
... | ... | @@ -413,6 +413,16 @@ |
413 | 413 |
return labels; |
414 | 414 |
} |
415 | 415 |
|
416 |
+ public Set<Long> getLabelIds() { |
|
417 |
+ Set<Long> labelIds = new HashSet<>(); |
|
418 |
+ |
|
419 |
+ for(IssueLabel label : this.labels){ |
|
420 |
+ labelIds.add(label.id); |
|
421 |
+ } |
|
422 |
+ |
|
423 |
+ return labelIds; |
|
424 |
+ } |
|
425 |
+ |
|
416 | 426 |
public List<TimelineItem> getTimeline() { |
417 | 427 |
List<TimelineItem> timelineItems = new ArrayList<>(); |
418 | 428 |
timelineItems.addAll(comments); |
--- app/utils/LabelSearchUtil.java
+++ app/utils/LabelSearchUtil.java
... | ... | @@ -35,6 +35,8 @@ |
35 | 35 |
import com.avaje.ebean.Query; |
36 | 36 |
import com.avaje.ebean.QueryResultVisitor; |
37 | 37 |
|
38 |
+import org.apache.commons.lang3.StringUtils; |
|
39 |
+ |
|
38 | 40 |
public class LabelSearchUtil { |
39 | 41 |
private static final String FIELD_NAME_ID = "id"; |
40 | 42 |
|
... | ... | @@ -53,7 +55,9 @@ |
53 | 55 |
String[] labelIds = request.queryString().get("labelIds"); |
54 | 56 |
if (labelIds != null) { |
55 | 57 |
for (String labelId : labelIds) { |
56 |
- set.add(Long.valueOf(labelId)); |
|
58 |
+ if(!StringUtils.isEmpty(labelId)) { |
|
59 |
+ set.add(Long.valueOf(labelId)); |
|
60 |
+ } |
|
57 | 61 |
} |
58 | 62 |
} |
59 | 63 |
return set; |
--- app/views/common/issueLabel.scala.html
... | ... | @@ -1,49 +0,0 @@ |
1 | -@** | |
2 | -* Yobi, Project Hosting SW | |
3 | -* | |
4 | -* Copyright 2013 NAVER Corp. | |
5 | -* http://yobi.io | |
6 | -* | |
7 | -* @Author Jihan Kim | |
8 | -* | |
9 | -* Licensed under the Apache License, Version 2.0 (the "License"); | |
10 | -* you may not use this file except in compliance with the License. | |
11 | -* You may obtain a copy of the License at | |
12 | -* | |
13 | -* http://www.apache.org/licenses/LICENSE-2.0 | |
14 | -* | |
15 | -* Unless required by applicable law or agreed to in writing, software | |
16 | -* distributed under the License is distributed on an "AS IS" BASIS, | |
17 | -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
18 | -* See the License for the specific language governing permissions and | |
19 | -* limitations under the License. | |
20 | -**@ | |
21 | -@() | |
22 | -<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yobi.Label.js")"></script> | |
23 | -<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yobi.LabelEditor.js")"></script> | |
24 | - | |
25 | -@**<!-- yobi.LabelEditor -->**@ | |
26 | -<script type="text/x-jquery-tmpl" id="tplYobiLabelEditor"> | |
27 | -<div class="control-group label-editor"> | |
28 | - <strong class="control-label">${labelNew}</strong> | |
29 | - <div id="custom-label" class="controls mt5"> | |
30 | - <div class="row-fluid"> | |
31 | - <div> | |
32 | - <input type="text" name="labelCategory" class="input-small labelInput" data-provider="typeahead" autocomplete="off" placeholder="${labelCategory}"> | |
33 | - </div> | |
34 | - <div> | |
35 | - <input type="text" name="labelName" class="input-small labelInput" placeholder="${labelName}" autocomplete="off"> | |
36 | - </div> | |
37 | - </div> | |
38 | - <div class="row-fluid colors-wrap"> | |
39 | - <div class="colors"></div> | |
40 | - <div class="colorInput"> | |
41 | - <input type="text" name="labelColor" class="input-small labelInput labelColor" placeholder="${labelCustomColor}"> | |
42 | - </div> | |
43 | - </div> | |
44 | - <div class="row-fluid mt5"> | |
45 | - <div class="span12"><button type="button" class="ybtn ybtn-default labelSubmit">${labelAdd}</button></div> | |
46 | - </div> | |
47 | - </div></div> | |
48 | -</script> | |
49 | -@**<!-- // yobi.LabelEditor -->**@ |
--- app/views/issue/create.scala.html
+++ app/views/issue/create.scala.html
... | ... | @@ -89,7 +89,10 @@ |
89 | 89 |
<dd> |
90 | 90 |
@defining(Milestone.findOpenMilestones(project.id)) { milestones => |
91 | 91 |
@if(milestones.isEmpty()) { |
92 |
- <a href="@routes.MilestoneApp.newMilestoneForm(project.owner, project.name)" target="_blank" class="ybtn ybtn-success ybtn-small">@Messages("milestone.menu.new")</a> |
|
92 |
+ <a href="@routes.MilestoneApp.newMilestoneForm(project.owner, project.name)" |
|
93 |
+ class="ybtn ybtn-small ybtn-fullsize" target="_blank"> |
|
94 |
+ @Messages("milestone.menu.new") |
|
95 |
+ </a> |
|
93 | 96 |
} else { |
94 | 97 |
<select id="milestoneId" name="milestoneId" |
95 | 98 |
data-toggle="select2" data-format="milestone"> |
... | ... | @@ -116,30 +119,7 @@ |
116 | 119 |
</dd> |
117 | 120 |
</dl> |
118 | 121 |
|
119 |
- <dl id="labels" class="issue-option"> |
|
120 |
- <dt> |
|
121 |
- @Messages("label") |
|
122 |
- @if(isProjectResourceCreatable(UserApp.currentUser, project, ResourceType.ISSUE_LABEL)){ |
|
123 |
- <a href="@routes.IssueLabelApp.labels(project.owner, project.name)" target="_blank" |
|
124 |
- class="ybtn ybtn-default ybtn-mini pull-right"><i class="yobicon-cog"></i></a> |
|
125 |
- } |
|
126 |
- </dt> |
|
127 |
- <dd> |
|
128 |
- <select id="issueLabels" multiple="multiple" data-toggle="select2" data-format="issuelabel" |
|
129 |
- data-dropdown-css-class="issue-labels" data-container-css-class="issue-labels" style="border:1px solid #ddd;"> |
|
130 |
- <option></option> |
|
131 |
- @IssueLabel.findByProject(project).groupBy(_.category).map { |
|
132 |
- case (category, labels) => { |
|
133 |
- <optgroup label="@category.name"> |
|
134 |
- @labels.map { label => |
|
135 |
- <option value="@label.id" data-category="@category.id">@label.name</option> |
|
136 |
- } |
|
137 |
- </optgroup> |
|
138 |
- } |
|
139 |
- } |
|
140 |
- </select> |
|
141 |
- </dd> |
|
142 |
- </dl> |
|
122 |
+ @partial_select_label(IssueLabel.findByProject(project), null, "issue-option") |
|
143 | 123 |
</div> |
144 | 124 |
</div> |
145 | 125 |
</div> |
--- app/views/issue/edit.scala.html
+++ app/views/issue/edit.scala.html
... | ... | @@ -110,7 +110,10 @@ |
110 | 110 |
<dd> |
111 | 111 |
@defining(issue.milestone != null) { hasMilestone => |
112 | 112 |
@if(Milestone.findByProjectId(project.id).isEmpty()) { |
113 |
- <a href="@routes.MilestoneApp.newMilestoneForm(project.owner, project.name)" target="_blank" class="ybtn ybtn-success ybtn-small">@Messages("milestone.menu.new")</a> |
|
113 |
+ <a href="@routes.MilestoneApp.newMilestoneForm(project.owner, project.name)" |
|
114 |
+ class="ybtn ybtn-small ybtn-fullsize" target="_blank"> |
|
115 |
+ @Messages("milestone.menu.new") |
|
116 |
+ </a> |
|
114 | 117 |
} else { |
115 | 118 |
<select id="milestoneId" name="milestoneId" |
116 | 119 |
data-toggle="select2" data-format="milestone"> |
... | ... | @@ -154,32 +157,7 @@ |
154 | 157 |
</dd> |
155 | 158 |
</dl> |
156 | 159 |
|
157 |
- <dl id="labels" class="issue-option"> |
|
158 |
- <dt> |
|
159 |
- @Messages("label") |
|
160 |
- @if(isProjectResourceCreatable(UserApp.currentUser, project, ResourceType.ISSUE_LABEL)){ |
|
161 |
- <a href="@routes.IssueLabelApp.labels(project.owner, project.name)" target="_blank" |
|
162 |
- class="ybtn ybtn-default ybtn-mini pull-right"><i class="yobicon-cog"></i></a> |
|
163 |
- } |
|
164 |
- </dt> |
|
165 |
- <dd> |
|
166 |
- <select id="issueLabels" multiple="multiple" data-toggle="select2" data-format="issuelabel" |
|
167 |
- data-dropdown-css-class="issue-labels" data-container-css-class="issue-labels" style="border:1px solid #ddd;"> |
|
168 |
- <option></option> |
|
169 |
- @IssueLabel.findByProject(project).groupBy(_.category).map { |
|
170 |
- case (category, labels) => { |
|
171 |
- <optgroup label="@category.name"> |
|
172 |
- @labels.map { label => |
|
173 |
- <option value="@label.id" data-category="@category.id" @if(issue.labels.contains(label)){ selected }> |
|
174 |
- @label.name |
|
175 |
- </option> |
|
176 |
- } |
|
177 |
- </optgroup> |
|
178 |
- } |
|
179 |
- } |
|
180 |
- </select> |
|
181 |
- </dd> |
|
182 |
- </dl> |
|
160 |
+ @partial_select_label(IssueLabel.findByProject(project), issue.getLabelIds, "issue-option") |
|
183 | 161 |
</div> |
184 | 162 |
</div> |
185 | 163 |
</div> |
--- app/views/issue/my_list.scala.html
+++ app/views/issue/my_list.scala.html
... | ... | @@ -35,43 +35,12 @@ |
35 | 35 |
@views.html.issue.my_partial_search(title, currentPage, param, project) |
36 | 36 |
</div> |
37 | 37 |
</div> |
38 |
- <div id="spin" style="position:absolute; top:35%; left:50%"></div> |
|
39 |
- <script type="text/javascript" src="/assets/javascripts/lib/spin.js"></script> |
|
40 | 38 |
<script type="text/javascript"> |
41 |
- $(document).ready(function(){ |
|
42 |
- $(".issue-label").on('click', function(event) { |
|
43 |
- window.location.href = $(this).attr("href"); |
|
44 |
- }); |
|
45 |
- var pjaxOptions = { |
|
46 |
- fragment:"div[pjax-container]", |
|
47 |
- timeout:3000 |
|
48 |
- }; |
|
49 |
- |
|
50 |
- if($.support.pjax) { |
|
51 |
- $.pjax.defaults.maxCacheLength = 0; |
|
52 |
- } |
|
53 |
- |
|
54 |
- $(document).on('click', 'a[pjax-page]', function(event) { |
|
55 |
- $.pjax.click(event, "div[pjax-container]", pjaxOptions) |
|
56 |
- }); |
|
57 |
- |
|
58 |
- $(document).on("submit", "form[name='search']", function(event) { |
|
59 |
- $.pjax.submit(event, "div[pjax-container]", pjaxOptions) |
|
60 |
- }); |
|
61 |
- |
|
62 |
- $(document).on('pjax:send', function() { |
|
63 |
- yobi.ui.Spinner.show(); |
|
64 |
- }); |
|
65 |
- |
|
66 |
- $(document).on('pjax:complete', function() { |
|
67 |
- yobi.ui.Spinner.hide(); |
|
68 |
- }); |
|
69 |
- $(document).on('pjax:end', function() { |
|
70 |
- $(".issue-label").on('click', function() { |
|
71 |
- window.location.href = $(this).attr("href"); |
|
72 |
- }); |
|
73 |
- }); |
|
74 |
- |
|
39 |
+ $(function(){ |
|
40 |
+ $yobi.loadModule("issue.List", { |
|
41 |
+ "welSearchForm": $("#search"), |
|
42 |
+ "elPagination" : $("#pagination") |
|
75 | 43 |
}); |
44 |
+ }); |
|
76 | 45 |
</script> |
77 | 46 |
} |
--- app/views/issue/my_partial_list.scala.html
+++ app/views/issue/my_partial_list.scala.html
... | ... | @@ -82,7 +82,7 @@ |
82 | 82 |
} |
83 | 83 |
|
84 | 84 |
@for(label <- issue.labels.toList.sortBy(r => (r.category.name, r.name))) { |
85 |
- <a href="@urlToList(issue.project, searchCondition.state)&labelIds=@label.id" class="label issue-label list-label" data-label-id="@label.id" data-color="@label.color" style="background:@label.color">@label.name</a> |
|
85 |
+ <a href="@urlToList(issue.project, searchCondition.state)&labelIds=@label.id" class="label issue-label list-label" data-label-id="@label.id" style="background:@label.color">@label.name</a> |
|
86 | 86 |
} |
87 | 87 |
</div> |
88 | 88 |
</div> |
+++ app/views/issue/my_partial_list_quicksearch.scala.html
... | ... | @@ -0,0 +1,53 @@ |
1 | +@** | |
2 | +* Yobi, Project Hosting SW | |
3 | +* | |
4 | +* Copyright 2014 NAVER Corp. | |
5 | +* http://yobi.io | |
6 | +* | |
7 | +* @Author Jihan Kim | |
8 | +* | |
9 | +* Licensed under the Apache License, Version 2.0 (the "License"); | |
10 | +* you may not use this file except in compliance with the License. | |
11 | +* You may obtain a copy of the License at | |
12 | +* | |
13 | +* http://www.apache.org/licenses/LICENSE-2.0 | |
14 | +* | |
15 | +* Unless required by applicable law or agreed to in writing, software | |
16 | +* distributed under the License is distributed on an "AS IS" BASIS, | |
17 | +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
18 | +* See the License for the specific language governing permissions and | |
19 | +* limitations under the License. | |
20 | +**@ | |
21 | +@(param:models.support.SearchCondition) | |
22 | + | |
23 | +<ul class="lst-stacked unstyled"> | |
24 | + @if(!UserApp.currentUser().isAnonymous()){ | |
25 | + <li @if(param.assigneeId == UserApp.currentUser().id){ class="active"}> | |
26 | + <a pjax-filter href="#" | |
27 | + data-author-id="" | |
28 | + data-assignee-id="@User.findByLoginId(session.get("loginId")).id" | |
29 | + data-milestone-id="@param.milestoneId" | |
30 | + data-mention-id=""> | |
31 | + @Messages("issue.list.assignedToMe") | |
32 | + </a> | |
33 | + </li> | |
34 | + <li @if(param.authorId == UserApp.currentUser().id){ class="active"}> | |
35 | + <a pjax-filter href="#" | |
36 | + data-author-id="@User.findByLoginId(session.get("loginId")).id" | |
37 | + data-assignee-id="" | |
38 | + data-milestone-id="@param.milestoneId" | |
39 | + data-mention-id=""> | |
40 | + @Messages("issue.list.authoredByMe") | |
41 | + </a> | |
42 | + </li> | |
43 | + <li @if(param.mentionId == UserApp.currentUser().id){ class="active"}> | |
44 | + <a pjax-filter href="#" | |
45 | + data-author-id="" | |
46 | + data-assignee-id="" | |
47 | + data-milestone-id="@param.milestoneId" | |
48 | + data-mention-id="@User.findByLoginId(session.get("loginId")).id"> | |
49 | + @Messages("issue.list.mentionedOfMe") | |
50 | + </a> | |
51 | + </li> | |
52 | + } | |
53 | +</ul> |
--- app/views/issue/my_partial_search.scala.html
+++ app/views/issue/my_partial_search.scala.html
... | ... | @@ -21,133 +21,81 @@ |
21 | 21 |
@(title: String, currentPage: com.avaje.ebean.Page[Issue], param:models.support.SearchCondition, project:Project) |
22 | 22 |
|
23 | 23 |
@import helper._ |
24 |
-@import utils.TemplateHelper._ |
|
25 |
-@import utils.AccessControl._ |
|
26 |
-@import scala.collection.immutable.Map |
|
27 |
-@import scala.collection.mutable.ArrayBuffer |
|
28 | 24 |
@import models.enumeration._ |
29 |
-@import org.apache.commons.lang.StringUtils |
|
30 |
-@import utils.HttpUtil._ |
|
31 |
- |
|
32 |
-@urlToList = {@routes.IssueApp.issues(project.owner, project.name, param.state, "html", currentPage.getPageIndex + 1)} |
|
33 |
-@paramForEveryone = @{ param.clone().setState(param.state).setAssigneeId(null).setAuthorId(null) } |
|
34 |
-@paramForIssuesAssignedToMe = @{ param.clone().setState(param.state).setAssigneeId(UserApp.currentUser().id).setAuthorId(null) } |
|
35 |
-@paramForIssuesAuthoredByMe = @{ param.clone().setState(param.state).setAssigneeId(null).setAuthorId(UserApp.currentUser().id) } |
|
36 |
- |
|
37 |
-@getPageListUrl(pageIndex:Integer) = {@routes.IssueApp.userIssues(param.state, "html", pageIndex + 1)} |
|
25 |
+@import utils.AccessControl._ |
|
26 |
+@import utils.TemplateHelper._ |
|
38 | 27 |
|
39 | 28 |
@makeFilterLink(fieldName:String, orderBy:String, orderDir:String, fieldText:String) = { |
40 | 29 |
@if(orderBy.equals(fieldName)) { |
41 | 30 |
<a href="#" orderBy="@orderBy" orderDir="@if(orderDir.equals("desc")){asc}else{desc}" class="filter active"><i class="ico btn-gray-arrow @if(orderDir.equals("desc")){ down }"></i>@fieldText</a> |
42 | 31 |
} else { |
43 |
- <a href="#" orderBy="@fieldName" orderDir="desc" class="filter"><i class="ico btn-gray-arrow down"></i>@fieldText</a> |
|
32 |
+ <a href="#" orderBy="@fieldName" orderDir="desc" class="filter"><i class="ico btn-gray-arrow down"></i>@fieldText</a> |
|
44 | 33 |
} |
45 | 34 |
} |
46 | 35 |
|
47 |
-@getTabLinkByState(state:State) = {@routes.IssueApp.issues(project.owner, project.name, state.state)@if(param.assigneeId != null){?assigneeId=@param.assigneeId}@if(param.authorId != null){&authorId=@param.authorId}@if(param.milestoneId != null){&milestoneId=@param.milestoneId}} |
|
36 |
+<div pjax-container class="row-fluid issue-list-wrap"> |
|
37 |
+ <div class="span2"> |
|
38 |
+ <div class="inner advanced"> |
|
39 |
+ @my_partial_list_quicksearch(param) |
|
48 | 40 |
|
49 |
- <div pjax-container class="row-fluid issue-list-wrap"> |
|
50 |
- <div class="span2"> |
|
51 |
- <div class="inner advanced"> |
|
52 |
- <ul class="lst-stacked unstyled"> |
|
53 |
- @if(!UserApp.currentUser().isAnonymous()){ |
|
54 |
- <li @if(param.assigneeId == UserApp.currentUser().id){ class="active"}> |
|
55 |
- <a pjax-filter href="#" mentionId="" authorId="" assigneeId="@User.findByLoginId(session.get("loginId")).id" milestoneId="@param.milestoneId">@Messages("issue.list.assignedToMe") |
|
56 |
- </a> |
|
57 |
- </li> |
|
58 |
- <li @if(param.authorId == UserApp.currentUser().id){ class="active"}> |
|
59 |
- <a pjax-filter href="#" mentionId="" authorId="@User.findByLoginId(session.get("loginId")).id" assigneeId="" milestoneId="@param.milestoneId">@Messages("issue.list.authoredByMe") |
|
60 |
- </a> |
|
61 |
- </li> |
|
62 |
- <li @if(param.mentionId == UserApp.currentUser().id){ class="active"}> |
|
63 |
- <a pjax-filter href="#" mentionId="@User.findByLoginId(session.get("loginId")).id" authorId="" assigneeId="" milestoneId="@param.milestoneId">@Messages("issue.list.mentionedOfMe") |
|
64 |
- </a> |
|
65 |
- </li> |
|
66 |
- } |
|
67 |
- </ul> |
|
41 |
+ <hr> |
|
68 | 42 |
|
69 |
- @if(param.milestoneId != null){ |
|
70 |
- @defining(Milestone.findById(param.milestoneId)){ milestone => |
|
71 |
- @if(milestone != null){ |
|
72 |
- <hr> |
|
73 |
- @views.html.milestone.partial_status(milestone, project) |
|
74 |
- } |
|
43 |
+ <form id="search" name="search" action="@routes.IssueApp.userIssues()" method="get"> |
|
44 |
+ <input type="hidden" name="orderBy" value="@param.orderBy"> |
|
45 |
+ <input type="hidden" name="orderDir" value="@param.orderDir"> |
|
46 |
+ <input type="hidden" name="state" value="@param.state"> |
|
47 |
+ <input type="hidden" name="authorId" value="@param.authorId" data-search="authorId"> |
|
48 |
+ <input type="hidden" name="assigneeId" value="@param.assigneeId" data-search="assigneeId"> |
|
49 |
+ <input type="hidden" name="mentionId" value="@param.mentionId" data-search="mentionId"> |
|
50 |
+ <div class="search"> |
|
51 |
+ <div class="search-bar"> |
|
52 |
+ <input name="filter" class="textbox full" type="text" value="@param.filter"> |
|
53 |
+ <button type="submit" class="search-btn"><i class="yobicon-search"></i></button> |
|
54 |
+ </div> |
|
55 |
+ </div> |
|
56 |
+ </form> |
|
57 |
+ |
|
58 |
+ @if(param.milestoneId != null){ |
|
59 |
+ @defining(Milestone.findById(param.milestoneId)){ milestone => |
|
60 |
+ @if(milestone != null){ |
|
61 |
+ <hr> |
|
62 |
+ @views.html.milestone.partial_status(milestone, project) |
|
75 | 63 |
} |
76 | 64 |
} |
77 |
- |
|
78 |
- <form id="search" name="search" action="@routes.IssueApp.userIssues()" method="get"> |
|
79 |
- <input type="hidden" name="orderBy" value="@param.orderBy"> |
|
80 |
- <input type="hidden" name="orderDir" value="@param.orderDir"> |
|
81 |
- <input type="hidden" name="state" value="@param.state"> |
|
82 |
- <input type="hidden" name="authorId" value="@param.authorId"> |
|
83 |
- <input type="hidden" name="assigneeId" value="@param.assigneeId"> |
|
84 |
- <input type="hidden" name="mentionId" value="@param.mentionId"> |
|
85 |
- <hr> |
|
86 |
- <div class="search"> |
|
87 |
- <div class="search-bar"> |
|
88 |
- <input name="filter" class="textbox full" type="text" value="@param.filter"> |
|
89 |
- <button type="submit" class="search-btn"><i class="yobicon-search"></i></button> |
|
90 |
- </div> |
|
91 |
- </div> |
|
92 |
- </form> |
|
93 |
- </div> |
|
94 |
- </div> |
|
95 |
- <div class="span10" id="span10"> |
|
96 |
- <ul class="nav nav-tabs nm"> |
|
97 |
- @for(state <- Array(State.OPEN, State.CLOSED)) { |
|
98 |
- <li @if(param.state == state.state) { class="active" } data-pjax> |
|
99 |
- <a href="#" state="@state.state"> |
|
100 |
- @Messages("issue.state." + state.name.toLowerCase) |
|
101 |
- <span class="num-badge">@Issue.countIssuesBy(param.clone.setState(state))</span> |
|
102 |
- </a> |
|
103 |
- </li> |
|
104 | 65 |
} |
105 |
- </ul> |
|
106 |
- |
|
107 |
- |
|
108 |
- @if(currentPage.getList.size() > 0){ |
|
109 |
- <div class="filter-wrap small-heights"> |
|
66 |
+ </div> |
|
67 |
+ </div> |
|
68 |
+ <div class="span10" id="span10"> |
|
69 |
+ <ul class="nav nav-tabs nm"> |
|
70 |
+ @for(state <- Array(State.OPEN, State.CLOSED)) { |
|
71 |
+ <li @if(param.state == state.state) { class="active" } data-pjax> |
|
72 |
+ <a href="#" state="@state.state"> |
|
73 |
+ @Messages("issue.state." + state.name.toLowerCase) |
|
74 |
+ <span class="num-badge">@Issue.countIssuesBy(param.clone.setState(state))</span> |
|
75 |
+ </a> |
|
76 |
+ </li> |
|
77 |
+ } |
|
78 |
+ </ul> |
|
79 |
+ @if(!currentPage.getList.isEmpty){ |
|
80 |
+ <div class="filter-wrap small-heights"> |
|
110 | 81 |
@if(currentPage.getList.size() > 1){ |
111 |
- <div class="filters pull-right"> |
|
112 |
- @makeFilterLink("dueDate", param.orderBy, param.orderDir, Messages("common.order.dueDate")) |
|
113 |
- @makeFilterLink("updatedDate", param.orderBy, param.orderDir, Messages("common.order.updatedDate")) |
|
114 |
- @makeFilterLink("createdDate", param.orderBy, param.orderDir, Messages("common.order.date")) |
|
115 |
- @makeFilterLink("numOfComments", param.orderBy, param.orderDir, Messages("common.order.comments")) |
|
116 |
- </div> |
|
117 |
- } |
|
118 |
- </div> |
|
82 |
+ |
|
83 |
+ <div class="filters pull-right"> |
|
84 |
+ @makeFilterLink("updatedDate", param.orderBy, param.orderDir, Messages("common.order.updatedDate")) |
|
85 |
+ @makeFilterLink("createdDate", param.orderBy, param.orderDir, Messages("common.order.date")) |
|
86 |
+ @makeFilterLink("numOfComments", param.orderBy, param.orderDir, Messages("common.order.comments")) |
|
87 |
+ </div> |
|
88 |
+ } |
|
89 |
+ </div> |
|
119 | 90 |
|
120 | 91 |
@my_partial_list(currentPage.getList, param, currentPage.getPageIndex, currentPage.getTotalPageCount, project) |
121 | 92 |
|
122 |
- <div class="pull-left" style="padding:10px;"> |
|
123 |
- </div> |
|
124 |
- <div id="pagination"><!-- pagination.js will fill here. --></div> |
|
93 |
+ <div id="pagination" data-total="@currentPage.getTotalPageCount"><!-- pagination.js will fill here. --></div> |
|
125 | 94 |
} else { |
126 | 95 |
<div class="error-wrap"> |
127 | 96 |
<i class="ico ico-err1"></i> |
128 | 97 |
<p>@Messages("issue.is.empty")</p> |
129 | 98 |
</div> |
130 | 99 |
} |
131 |
- </div> |
|
132 |
- <script type="text/javascript"> |
|
133 |
- $(document).ready(function(){ |
|
134 |
- $("a[pjax-filter]" ).bind("click",function(){ |
|
135 |
- $("input[name='authorId']").val($(this).attr("authorId")); |
|
136 |
- $("input[name='assigneeId']").val($(this).attr("assigneeId")); |
|
137 |
- $("input[name='mentionId']").val($(this).attr("mentionId")); |
|
138 |
- $("#search" ).submit(); |
|
139 |
- }); |
|
140 |
- $yobi.loadModule("issue.List", { |
|
141 |
- "welSearchOrder": $("a[orderBy]"), |
|
142 |
- "welSearchState": $("a[state]"), |
|
143 |
- "welSearchAuthor": $("div[data-name='authorId']"), |
|
144 |
- "welSearchAssignee": $("div[data-name='assigneeId']"), |
|
145 |
- "welSearchMilestone": $("div[data-name='milestoneId']"), |
|
146 |
- "welSearchForm":$("form[name='search']"), |
|
147 |
- "welFilter": $("a[pjax-filter]"), |
|
148 |
- "elPagination": $("#pagination"), |
|
149 |
- "nTotalPages" : @currentPage.getTotalPageCount |
|
150 |
- }); |
|
151 |
- }); |
|
152 |
- </script> |
|
153 | 100 |
</div> |
101 |
+</div> |
--- app/views/issue/partial_assignee.scala.html
+++ app/views/issue/partial_assignee.scala.html
... | ... | @@ -23,6 +23,7 @@ |
23 | 23 |
@import utils.AccessControl._ |
24 | 24 |
|
25 | 25 |
<select id="assignee" name="assignee.user.id" |
26 |
+ data-field-name="assignee.id" |
|
26 | 27 |
data-toggle="select2" data-format="user"> |
27 | 28 |
<option value="@User.anonymous.id" @if(issue == null || issue.assignee == null){selected}>@Messages("issue.noAssignee")</option> |
28 | 29 |
@if(isAllowed(UserApp.currentUser(), project.asResource(), Operation.ASSIGN_ISSUE)) { |
--- app/views/issue/partial_list.scala.html
+++ app/views/issue/partial_list.scala.html
... | ... | @@ -85,7 +85,7 @@ |
85 | 85 |
} |
86 | 86 |
|
87 | 87 |
@for(label <- issue.labels.toList.sortBy(r => (r.category.name, r.name))) { |
88 |
- <a href="#" class="label issue-label list-label" data-label-id="@label.id" data-color="@label.color" style="background:@label.color">@label.name</a> |
|
88 |
+ <a href="#" class="label issue-label list-label" data-category-id="@label.category.id" data-label-id="@label.id" data-color="@label.color" style="background:@label.color">@label.name</a> |
|
89 | 89 |
} |
90 | 90 |
</div> |
91 | 91 |
</label> |
--- app/views/issue/partial_list_quicksearch.scala.html
+++ app/views/issue/partial_list_quicksearch.scala.html
... | ... | @@ -26,18 +26,30 @@ |
26 | 26 |
|
27 | 27 |
<ul class="lst-stacked unstyled"> |
28 | 28 |
<li @if(param.assigneeId == null && param.authorId == null){class="active"}> |
29 |
- <a pjax-filter href="#" authorId="" assigneeId="" milestoneId="@param.milestoneId">@Messages("issue.list.all") |
|
29 |
+ <a pjax-filter href="#" |
|
30 |
+ data-assignee-id="" |
|
31 |
+ data-author-id="" |
|
32 |
+ data-milestone-id="@param.milestoneId"> |
|
33 |
+ @Messages("issue.list.all") |
|
30 | 34 |
<span class="num-badge pull-right">@Issue.countIssuesBy(project.id, paramForEveryone)</span> |
31 | 35 |
</a> |
32 | 36 |
</li> |
33 | 37 |
@if(!UserApp.currentUser().isSiteManager){ |
34 | 38 |
<li @if(param.assigneeId == UserApp.currentUser().id){ class="active"}> |
35 |
- <a pjax-filter href="#" authorId="" assigneeId="@User.findByLoginId(session.get("loginId")).id" milestoneId="@param.milestoneId">@Messages("issue.list.assignedToMe") |
|
39 |
+ <a pjax-filter href="#" |
|
40 |
+ data-assignee-id="@User.findByLoginId(session.get("loginId")).id" |
|
41 |
+ data-author-id="" |
|
42 |
+ data-milestone-id="@param.milestoneId"> |
|
43 |
+ @Messages("issue.list.assignedToMe") |
|
36 | 44 |
<span class="num-badge pull-right">@Issue.countIssuesBy(project.id, paramForIssuesAssignedToMe)</span> |
37 | 45 |
</a> |
38 | 46 |
</li> |
39 | 47 |
<li @if(param.authorId == UserApp.currentUser().id){ class="active"}> |
40 |
- <a pjax-filter href="#" authorId="@User.findByLoginId(session.get("loginId")).id" assigneeId="" milestoneId="@param.milestoneId">@Messages("issue.list.authoredByMe") |
|
48 |
+ <a pjax-filter href="#" |
|
49 |
+ data-assignee-id="" |
|
50 |
+ data-author-id="@User.findByLoginId(session.get("loginId")).id" |
|
51 |
+ data-milestone-id="@param.milestoneId"> |
|
52 |
+ @Messages("issue.list.authoredByMe") |
|
41 | 53 |
<span class="num-badge pull-right">@Issue.countIssuesBy(project.id, paramForIssuesAuthoredByMe)</span> |
42 | 54 |
</a> |
43 | 55 |
</li> |
--- app/views/issue/partial_massupdate.scala.html
+++ app/views/issue/partial_massupdate.scala.html
... | ... | @@ -72,7 +72,7 @@ |
72 | 72 |
|
73 | 73 |
<div id="assignee" class="btn-group" data-name="assignee.id"> |
74 | 74 |
<button class="btn dropdown-toggle medium" data-toggle="dropdown" disabled="disabled"> |
75 |
- <span class="d-label">@Messages("issue.update.assignee")</span> |
|
75 |
+ <span class="d-label">@Messages("issue.update.assignee.id")</span> |
|
76 | 76 |
<span class="d-caret"><span class="caret"></span></span> |
77 | 77 |
</button> |
78 | 78 |
<ul class="dropdown-menu mass-update-list"> |
... | ... | @@ -98,7 +98,7 @@ |
98 | 98 |
@if(project.menuSetting.milestone) { |
99 | 99 |
<div id="milestone" class="btn-group" data-name="milestone.id"> |
100 | 100 |
<button class="btn dropdown-toggle medium" data-toggle="dropdown" disabled="disabled"> |
101 |
- <span class="d-label">@Messages("issue.update.milestone")</span> |
|
101 |
+ <span class="d-label">@Messages("issue.update.milestone.id")</span> |
|
102 | 102 |
<span class="d-caret"><span class="caret"></span></span> |
103 | 103 |
</button> |
104 | 104 |
<ul class="dropdown-menu mass-update-list"> |
--- app/views/issue/partial_searchform.scala.html
+++ app/views/issue/partial_searchform.scala.html
... | ... | @@ -29,7 +29,7 @@ |
29 | 29 |
<hr> |
30 | 30 |
<div class="search"> |
31 | 31 |
<div class="search-bar"> |
32 |
- <input name="filter" class="textbox full" type="text" value="@param.filter"> |
|
32 |
+ <input name="filter" class="textbox full" type="text" value="@param.filter" data-search="filter"> |
|
33 | 33 |
<button type="submit" class="search-btn"><i class="yobicon-search"></i></button> |
34 | 34 |
</div> |
35 | 35 |
</div> |
... | ... | @@ -38,7 +38,7 @@ |
38 | 38 |
<dl class="issue-option"> |
39 | 39 |
<dt>@Messages("issue.author")</dt> |
40 | 40 |
<dd> |
41 |
- <select id="authorId" name="authorId" data-toggle="select2" data-format="user"> |
|
41 |
+ <select id="authorId" name="authorId" data-toggle="select2" data-format="user" data-search="authorId"> |
|
42 | 42 |
<option value="">@Messages("common.order.all")</option> |
43 | 43 |
@if(ProjectUser.isMember(UserApp.currentUser().id, project.id)){ |
44 | 44 |
<option value="@UserApp.currentUser().id">@Messages("issue.list.authoredByMe")</option> |
... | ... | @@ -60,7 +60,7 @@ |
60 | 60 |
<dl class="issue-option"> |
61 | 61 |
<dt>@Messages("issue.assignee")</dt> |
62 | 62 |
<dd> |
63 |
- <select id="assigneeId" name="assigneeId" data-toggle="select2" data-format="user"> |
|
63 |
+ <select id="assigneeId" name="assigneeId" data-toggle="select2" data-format="user" data-search="assigneeId"> |
|
64 | 64 |
<option value="">@Messages("common.order.all")</option> |
65 | 65 |
<option value="@User.anonymous.id" @if(param.assigneeId != null && param.assigneeId == User.anonymous.id){ selected }>@Messages("issue.noAssignee")</option> |
66 | 66 |
@if(ProjectUser.isMember(UserApp.currentUser().id, project.id)){ |
... | ... | @@ -84,7 +84,7 @@ |
84 | 84 |
<dl class="issue-option"> |
85 | 85 |
<dt>@Messages("milestone")</dt> |
86 | 86 |
<dd> |
87 |
- <select id="milestoneId" name="milestoneId" data-toggle="select2" data-format="milestone"> |
|
87 |
+ <select id="milestoneId" name="milestoneId" data-toggle="select2" data-format="milestone" data-search="milestoneId"> |
|
88 | 88 |
<option value="">@Messages("milestone.state.all")</option> |
89 | 89 |
<option value="@Milestone.NULL_MILESTONE_ID" |
90 | 90 |
@if(param.milestoneId != null && param.milestoneId == Milestone.NULL_MILESTONE_ID){ |
... | ... | @@ -128,36 +128,22 @@ |
128 | 128 |
<dl class="issue-option"> |
129 | 129 |
<dt>@Messages("issue.dueDate")</dt> |
130 | 130 |
<dd class="search search-bar"> |
131 |
- <input type="text" id="issueDueDate" data-toggle="calendar" name="dueDate" class="textbox full" value="@param.getDueDateString"> |
|
131 |
+ <input id="issueDueDate" type="text" name="dueDate" class="textbox full" value="@param.getDueDateString" data-toggle="calendar"> |
|
132 | 132 |
<button type="button" class="search-btn"><i class="yobicon-calendar2"></i></button> |
133 | 133 |
</dd> |
134 | 134 |
</dl> |
135 | 135 |
|
136 |
- <dl id="labels" class="issue-option labels-wrap"> |
|
137 |
- <dt> |
|
138 |
- @Messages("label") |
|
139 |
- @if(isProjectResourceCreatable(UserApp.currentUser, project, ResourceType.ISSUE_LABEL)){ |
|
140 |
- <a href="@routes.IssueLabelApp.labels(project.owner, project.name)" target="_blank" |
|
141 |
- class="ybtn ybtn-default ybtn-mini pull-right"><i class="yobicon-cog"></i></a> |
|
142 |
- } |
|
143 |
- </dt> |
|
144 |
- <dd> |
|
145 |
- <select id="labelIds" name="labelIds" multiple="multiple" data-toggle="select2" data-format="issuelabel" |
|
146 |
- data-dropdown-css-class="issue-labels" data-container-css-class="issue-labels" style="border:1px solid #ddd;"> |
|
147 |
- <option></option> |
|
148 |
- @IssueLabel.findByProject(project).groupBy(_.category).map { |
|
149 |
- case (category, labels) => { |
|
150 |
- <optgroup label="@category.name"> |
|
151 |
- @labels.map { label => |
|
152 |
- <option value="@label.id" data-category="@category.id" @if(param.labelIds != null && param.labelIds.contains(label.id)){ selected }> |
|
153 |
- @label.name |
|
154 |
- </option> |
|
155 |
- } |
|
156 |
- </optgroup> |
|
157 |
- } |
|
158 |
- } |
|
159 |
- </select> |
|
160 |
- </dd> |
|
161 |
- </dl> |
|
136 |
+ @defining(IssueLabel.findByProject(project)){ labels => |
|
137 |
+ <div class="labels-wrap"> |
|
138 |
+ @if(isProjectResourceCreatable(UserApp.currentUser, project, ResourceType.ISSUE_LABEL)){ |
|
139 |
+ <a href="@routes.IssueLabelApp.labels(project.owner, project.name)" class="ybtn ybtn-default ybtn-mini pull-right"> |
|
140 |
+ <i class="yobicon-cog vmiddle"></i> |
|
141 |
+ @if(labels.isEmpty){<span class="vmiddle" style="margin-left:2px;">@Messages("label.manage")</span>} |
|
142 |
+ </a> |
|
143 |
+ } |
|
144 |
+ |
|
145 |
+ @partial_select_label(labels, param.labelIds, "issue-option") |
|
146 |
+ </div> |
|
147 |
+ } |
|
162 | 148 |
</div> |
163 | 149 |
</form> |
+++ app/views/issue/partial_select_label.scala.html
... | ... | @@ -0,0 +1,53 @@ |
1 | +@** | |
2 | +* Yobi, Project Hosting SW | |
3 | +* | |
4 | +* Copyright 2014 NAVER Corp. | |
5 | +* http://yobi.io | |
6 | +* | |
7 | +* @Author Jihan Kim | |
8 | +* | |
9 | +* Licensed under the Apache License, Version 2.0 (the "License"); | |
10 | +* you may not use this file except in compliance with the License. | |
11 | +* You may obtain a copy of the License at | |
12 | +* | |
13 | +* http://www.apache.org/licenses/LICENSE-2.0 | |
14 | +* | |
15 | +* Unless required by applicable law or agreed to in writing, software | |
16 | +* distributed under the License is distributed on an "AS IS" BASIS, | |
17 | +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
18 | +* See the License for the specific language governing permissions and | |
19 | +* limitations under the License. | |
20 | +**@ | |
21 | +@(labels:List[models.IssueLabel], selectedLabelIds:Set[Long] = null, wrapperCSSClassName:String = "") | |
22 | + | |
23 | +@if(!labels.isEmpty){ | |
24 | +<dl class="@wrapperCSSClassName"> | |
25 | + <dt> | |
26 | + @Messages("label") | |
27 | + </dt> | |
28 | + <dd> | |
29 | + <select name="labelIds" multiple="multiple" data-search="labelIds" | |
30 | + data-toggle="select2" data-format="issuelabel" data-allow-clear="true" data-close-on-select="false" | |
31 | + data-dropdown-css-class="issue-labels" data-container-css-class="issue-labels bordered" | |
32 | + data-placeholder="@Messages("label.select")"> | |
33 | + <option></option> | |
34 | + @labels.groupBy(_.category).map { | |
35 | + case (category, labels) => { | |
36 | + <optgroup label="@category.name" | |
37 | + data-category-id="@category.id" | |
38 | + data-category-is-exclusive="@category.isExclusive"> | |
39 | + @labels.map { label => | |
40 | + <option value="@label.id" | |
41 | + data-category-id="@category.id" | |
42 | + data-category-is-exclusive="@category.isExclusive" | |
43 | + @if(selectedLabelIds != null && selectedLabelIds.contains(label.id)){ selected }> | |
44 | + @label.name | |
45 | + </option> | |
46 | + } | |
47 | + </optgroup> | |
48 | + } | |
49 | + } | |
50 | + </select> | |
51 | + </dd> | |
52 | +</dl> | |
53 | +} |
+++ app/views/issue/partial_show_selected_label.scala.html
... | ... | @@ -0,0 +1,37 @@ |
1 | +@** | |
2 | +* Yobi, Project Hosting SW | |
3 | +* | |
4 | +* Copyright 2014 NAVER Corp. | |
5 | +* http://yobi.io | |
6 | +* | |
7 | +* @Author Jihan Kim | |
8 | +* | |
9 | +* Licensed under the Apache License, Version 2.0 (the "License"); | |
10 | +* you may not use this file except in compliance with the License. | |
11 | +* You may obtain a copy of the License at | |
12 | +* | |
13 | +* http://www.apache.org/licenses/LICENSE-2.0 | |
14 | +* | |
15 | +* Unless required by applicable law or agreed to in writing, software | |
16 | +* distributed under the License is distributed on an "AS IS" BASIS, | |
17 | +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
18 | +* See the License for the specific language governing permissions and | |
19 | +* limitations under the License. | |
20 | +**@ | |
21 | +@(labels:List[models.IssueLabel], listLink:String = "") | |
22 | + | |
23 | +@if(!labels.isEmpty){ | |
24 | + <dl> | |
25 | + <dt> | |
26 | + @Messages("label") | |
27 | + </dt> | |
28 | + <dd> | |
29 | + @for(label <- labels){ | |
30 | + <a href='@listLink&labelIds=@label.id' class="label issue-label active static" | |
31 | + data-label-id="@label.id" style="background:@label.color"> | |
32 | + @label.name | |
33 | + </a> | |
34 | + } | |
35 | + </dd> | |
36 | + </dl> | |
37 | +} |
--- app/views/issue/view.scala.html
+++ app/views/issue/view.scala.html
... | ... | @@ -76,65 +76,106 @@ |
76 | 76 |
<!--board-body--> |
77 | 77 |
<div class="board-body row-fluid"> |
78 | 78 |
<div class="span9"> |
79 |
- @if(StringUtils.isEmpty(issue.body)){ |
|
80 |
- <div class="content empty-content"></div> |
|
81 |
- } else { |
|
82 |
- <div class="markdown-loader"> |
|
83 |
- <i class=" yobicon-loading2"></i> Loading.... |
|
79 |
+ <div class="author-info"> |
|
80 |
+ <a href="@routes.UserApp.userInfo(issue.authorLoginId)" class="usf-group"> |
|
81 |
+ <span class="avatar-wrap smaller"> |
|
82 |
+ <img src="@User.findByLoginId(issue.authorLoginId).avatarUrl" width="20" height="20"> |
|
83 |
+ </span> |
|
84 |
+ @if(issue.authorLoginId != null){ |
|
85 |
+ <strong class="name">@issue.authorName</strong> |
|
86 |
+ <span class="loginid"> <strong>@{"@"}</strong>@issue.authorLoginId</span> |
|
87 |
+ } else { |
|
88 |
+ <strong class="name">@Messages("issue.noAuthor")</strong> |
|
89 |
+ } |
|
90 |
+ </a> |
|
84 | 91 |
</div> |
85 |
- <div class="content markdown-wrap markdown-before" markdown="true">@issue.body</div> |
|
92 |
+ |
|
93 |
+ @if(StringUtils.isEmpty(issue.body)){ |
|
94 |
+ <div class="content empty-content"></div> |
|
95 |
+ } else { |
|
96 |
+ <div class="markdown-loader"> |
|
97 |
+ <i class="yobicon-loading2"></i> @Messages("site.massMail.loading") |
|
98 |
+ </div> |
|
99 |
+ <div class="content markdown-wrap markdown-before" markdown="true">@issue.body</div> |
|
86 | 100 |
} |
87 | 101 |
<div class="attachments" id="attachments" data-resource-type="@ResourceType.ISSUE_POST" data-resource-id="@issue.id"></div> |
102 |
+ |
|
103 |
+ <div class="board-actrow right-txt"> |
|
104 |
+ <div class="pull-left"> |
|
105 |
+ <div> |
|
106 |
+ @if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.WATCH)) { |
|
107 |
+ <button id="watch-button" type="button" class="ybtn @if(issue.getWatchers.contains(UserApp.currentUser())) {ybtn-watching}" |
|
108 |
+ data-toggle="button" data-watching="@if(issue.getWatchers.contains(UserApp.currentUser())){true}else{false}"> |
|
109 |
+ @if(issue.getWatchers.contains(UserApp.currentUser())) { |
|
110 |
+ @Messages("project.unwatch") |
|
111 |
+ } else { |
|
112 |
+ @Messages("project.watch") |
|
113 |
+ } |
|
114 |
+ </button> |
|
115 |
+ } |
|
116 |
+ |
|
117 |
+ <div id="vote" class="vote-wrap"> |
|
118 |
+ @if(isResourceCreatable(User.findByLoginId(session.get("loginId")), issue.asResource(), ResourceType.ISSUE_COMMENT)) { |
|
119 |
+ <a href="@urlToVote" class="ybtn @if(issue.isVotedBy(UserApp.currentUser)){ybtn-watching}" title="@getVoteButtonTitle" |
|
120 |
+ data-request-method="post" data-toggle="tooltip"> |
|
121 |
+ <span class="heart"><i class="yobicon-hearts"></i></span> |
|
122 |
+ <span class="desc">@Messages("issue.vote")</span> |
|
123 |
+ @if(issue.voters.size > 0) { |
|
124 |
+ <strong class="count">@issue.voters.size</strong> |
|
125 |
+ } |
|
126 |
+ </a> |
|
127 |
+ } else { |
|
128 |
+ <span class="ybtn ybtn-disabled" style="color:#777;" data-toggle="tooltip" title="@Messages("user.login.alert")" data-login="required"> |
|
129 |
+ <span class="heart"><i class="yobicon-hearts"></i></span> |
|
130 |
+ <span class="desc">@Messages("issue.vote")</span> |
|
131 |
+ @if(issue.voters.size > 0) { |
|
132 |
+ <strong class="count">@issue.voters.size</strong> |
|
133 |
+ } |
|
134 |
+ </span> |
|
135 |
+ } |
|
136 |
+ |
|
137 |
+ @if(issue.voters.size > 0){ |
|
138 |
+ @partial_voters(issue, 3) |
|
139 |
+ } |
|
140 |
+ </div> |
|
141 |
+ </div> |
|
142 |
+ </div> |
|
143 |
+ |
|
144 |
+ @if(issue.canBeDeleted) { |
|
145 |
+ @if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.DELETE)) { |
|
146 |
+ <a href="#deleteConfirm" data-toggle="modal" class="ybtn ybtn-danger">@Messages("button.delete")</a> |
|
147 |
+ } |
|
148 |
+ } else { |
|
149 |
+ <button class="ybtn ybtn-disabled" data-toggle="tooltip" data-placement="top" title="@Messages("issue.can.not.be.deleted")">@Messages("button.delete")</button> |
|
150 |
+ } |
|
151 |
+ |
|
152 |
+ @if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.UPDATE)) { |
|
153 |
+ <a href="@routes.IssueApp.editIssueForm(project.owner, project.name, issue.getNumber)" class="ybtn">@Messages("button.edit")</a> |
|
154 |
+ } |
|
155 |
+ </div> |
|
156 |
+ |
|
157 |
+ @** Comment **@ |
|
158 |
+ <div id="comments" class="board-comment-wrap"> |
|
159 |
+ <div id="timeline"> |
|
160 |
+ <div class="timeline-list"> |
|
161 |
+ @partial_comments(project, issue) |
|
162 |
+ </div> |
|
163 |
+ </div> |
|
164 |
+ @common.commentForm(issue.asResource(), ResourceType.ISSUE_COMMENT, routes.IssueApp.newComment(project.owner, project.name, issue.getNumber).toString()) |
|
165 |
+ </div> |
|
166 |
+ @** // Comment **@ |
|
88 | 167 |
</div> |
89 | 168 |
|
90 | 169 |
<div class="span3 mb20"> |
91 |
- <div class="author-info pull-right mr10"> |
|
92 |
- <form id="issueUpdateForm" action="@routes.IssueApp.massUpdate(project.owner, project.name)" method="post" class="frm-wrap"> |
|
170 |
+ <div class="issue-info"> |
|
171 |
+ <form id="issueUpdateForm" action="@routes.IssueApp.massUpdate(project.owner, project.name)" method="post"> |
|
93 | 172 |
<input type="hidden" name="issues[0].id" value="@issue.id" /> |
94 |
- |
|
95 |
- @**<!-- author -->**@ |
|
96 |
- <dl class="author"> |
|
97 |
- <dt>@Messages("issue.author")</dt> |
|
98 |
- <dd style="padding:5px 10px;"> |
|
99 |
- <a href="@routes.UserApp.userInfo(issue.authorLoginId)" class="usf-group"> |
|
100 |
- <span class="avatar-wrap smaller"> |
|
101 |
- <img src="@User.findByLoginId(issue.authorLoginId).avatarUrl" width="20" height="20"> |
|
102 |
- </span> |
|
103 |
- @if(issue.authorLoginId != null){ |
|
104 |
- <strong class="name">@issue.authorName</strong> |
|
105 |
- <span class="loginid"> <strong>@{"@"}</strong>@issue.authorLoginId</span> |
|
106 |
- } else { |
|
107 |
- <strong class="name">@Messages("issue.noAuthor")</strong> |
|
108 |
- } |
|
109 |
- </a> |
|
110 |
- </dd> |
|
111 |
- </dl> |
|
112 |
- @**<!-- // -->**@ |
|
113 |
- |
|
114 |
- @**<!-- state -->**@ |
|
115 |
- <!-- |
|
116 |
- <dl> |
|
117 |
- <dt>@Messages("issue.state")</dt> |
|
118 |
- <dd> |
|
119 |
- <div id="state" class="btn-group" data-name="state"> |
|
120 |
- <button class="btn dropdown-toggle large" data-toggle="dropdown"> |
|
121 |
- <span class="d-label">@Messages("issue.state")</span> |
|
122 |
- <span class="d-caret"><span class="caret"></span></span> |
|
123 |
- </button> |
|
124 |
- <ul class="dropdown-menu"> |
|
125 |
- <li data-value="@State.OPEN.name" @if(issue.state == State.OPEN){data-selected="true" class="active"}><a>@Messages("issue.state.open")</a></li> |
|
126 |
- <li data-value="@State.CLOSED.name" @if(issue.state == State.CLOSED){data-selected="true" class="active"}><a>@Messages("issue.state.closed")</a></li> |
|
127 |
- </ul> |
|
128 |
- </div> |
|
129 |
- </dd> |
|
130 |
- </dl> |
|
131 |
- --> |
|
132 | 173 |
|
133 | 174 |
@**<!-- assignee -->**@ |
134 | 175 |
<dl> |
135 | 176 |
<dt>@Messages("issue.assignee")</dt> |
136 | 177 |
|
137 |
- <dd style="padding:5px 10px;"> |
|
178 |
+ <dd> |
|
138 | 179 |
@defining(issue.assigneeName != null) { isAssigned => |
139 | 180 |
@if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.UPDATE)) { |
140 | 181 |
@partial_assignee(project, issue) |
... | ... | @@ -162,13 +203,12 @@ |
162 | 203 |
@if(project.menuSetting.milestone) { |
163 | 204 |
<dl> |
164 | 205 |
<dt>@Messages("milestone")</dt> |
165 |
- <dd style="padding:5px 10px;"> |
|
206 |
+ <dd> |
|
166 | 207 |
@if(Milestone.findByProjectId(project.id).isEmpty()){ |
167 |
- @if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.UPDATE)) { |
|
168 |
- <a href="@routes.MilestoneApp.newMilestoneForm(project.owner, project.name)" target="_blank" class="ybtn ybtn-success ybtn-small">@Messages("milestone.menu.new")</a> |
|
169 |
- } else { |
|
170 |
- @Messages("issue.noMilestone") |
|
171 |
- } |
|
208 |
+ <a href="@routes.MilestoneApp.newMilestoneForm(project.owner, project.name)" |
|
209 |
+ class="ybtn ybtn-small ybtn-fullsize" target="_blank"> |
|
210 |
+ @Messages("milestone.menu.new") |
|
211 |
+ </a> |
|
172 | 212 |
} else { |
173 | 213 |
@defining(issue.milestone != null) { hasMilestone => |
174 | 214 |
@if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.UPDATE)) { |
... | ... | @@ -213,7 +253,7 @@ |
213 | 253 |
} |
214 | 254 |
@**<!-- // -->**@ |
215 | 255 |
|
216 |
- <dl class="issue-option"> |
|
256 |
+ <dl> |
|
217 | 257 |
<dt> |
218 | 258 |
@Messages("issue.dueDate") |
219 | 259 |
@if(issue.dueDate != null) { |
... | ... | @@ -226,10 +266,10 @@ |
226 | 266 |
</span> |
227 | 267 |
} |
228 | 268 |
</dt> |
229 |
- <dd style="padding:5px 10px;"> |
|
269 |
+ <dd> |
|
230 | 270 |
@if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.UPDATE)) { |
231 | 271 |
<div class="search search-bar"> |
232 |
- <input type="text" id="issueDueDate" data-toggle="calendar" data-oDueDate="@issue.getDueDateString" name="dueDate" class="textbox full" value="@issue.getDueDateString"> |
|
272 |
+ <input type="text" name="dueDate" value="@issue.getDueDateString" class="textbox full" autocomplete="off" data-toggle="calendar"> |
|
233 | 273 |
<button type="button" class="search-btn"><i class="yobicon-calendar2"></i></button> |
234 | 274 |
</div> |
235 | 275 |
} else { |
... | ... | @@ -244,36 +284,11 @@ |
244 | 284 |
|
245 | 285 |
@**<!-- labels -->**@ |
246 | 286 |
@if(!IssueLabel.findByProject(project).isEmpty){ |
247 |
- <dl class="labels-wrap"> |
|
248 |
- <dt> |
|
249 |
- @Messages("label") |
|
250 |
- <button type="button" class="ybtn ybtn-mini edit-button" style="margin-left:10px;"><i class="yobicon-edit vmiddle"></i></button> |
|
251 |
- </dt> |
|
252 |
- <dd> |
|
253 |
- <dd style="padding:5px 10px; line-height:30px; @if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.UPDATE)){padding-left:0;}"> |
|
254 |
- @if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.UPDATE)) { |
|
255 |
- <select id="issueLabels" multiple="multiple" data-toggle="select2" data-format="issuelabel" |
|
256 |
- data-dropdown-css-class="issue-labels" data-container-css-class="issue-labels"> |
|
257 |
- <option></option> |
|
258 |
- @IssueLabel.findByProject(project).groupBy(_.category).map { |
|
259 |
- case (category, labels) => { |
|
260 |
- <optgroup label="@category.name"> |
|
261 |
- @labels.map { label => |
|
262 |
- <option value="@label.id" data-category="@category.id" @if(issue.labels.contains(label)){ selected }> |
|
263 |
- @label.name |
|
264 |
- </option> |
|
265 |
- } |
|
266 |
- </optgroup> |
|
267 |
- } |
|
268 |
- } |
|
269 |
- </select> |
|
270 |
- } else { |
|
271 |
- @for(label <- issue.labels.toList.sortBy(r => (r.category.name, r.name))) { |
|
272 |
- <a href='@routes.IssueApp.issues(project.owner, project.name, issue.state.state(), "html", 1)&labelIds=@label.id' class="label issue-label active static" data-label-id="@label.id" data-color="@label.color" style="background:@label.color">@label.name</a> |
|
273 |
- } |
|
274 |
- } |
|
275 |
- </dd> |
|
276 |
- </dl> |
|
287 |
+ @if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.UPDATE)){ |
|
288 |
+ @partial_select_label(IssueLabel.findByProject(project), issue.getLabelIds) |
|
289 |
+ } else { |
|
290 |
+ @partial_show_selected_label(issue.labels.toList, routes.IssueApp.issues(project.owner, project.name, issue.state.state(), "html", 1).toString) |
|
291 |
+ } |
|
277 | 292 |
} |
278 | 293 |
@**<!-- // -->**@ |
279 | 294 |
</form> |
... | ... | @@ -281,74 +296,11 @@ |
281 | 296 |
</div> |
282 | 297 |
</div> |
283 | 298 |
|
284 |
- <div class="board-footer board-actrow"> |
|
285 |
- <div class="pull-left"> |
|
286 |
- <div> |
|
287 |
- @if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.WATCH)) { |
|
288 |
- <button id="watch-button" type="button" class="ybtn @if(issue.getWatchers.contains(UserApp.currentUser())) {ybtn-watching}" |
|
289 |
- data-toggle="button" data-watching="@if(issue.getWatchers.contains(UserApp.currentUser())){true}else{false}"> |
|
290 |
- @if(issue.getWatchers.contains(UserApp.currentUser())) { |
|
291 |
- @Messages("project.unwatch") |
|
292 |
- } else { |
|
293 |
- @Messages("project.watch") |
|
294 |
- } |
|
295 |
- </button> |
|
296 |
- } |
|
297 | 299 |
|
298 |
- <div id="vote" class="vote-wrap"> |
|
299 |
- @if(isResourceCreatable(User.findByLoginId(session.get("loginId")), issue.asResource(), ResourceType.ISSUE_COMMENT)) { |
|
300 |
- <a href="@urlToVote" class="ybtn @if(issue.isVotedBy(UserApp.currentUser)){ybtn-watching}" title="@getVoteButtonTitle" |
|
301 |
- data-request-method="post" data-toggle="tooltip"> |
|
302 |
- <span class="heart"><i class="yobicon-hearts"></i></span> |
|
303 |
- <span class="desc">@Messages("issue.vote")</span> |
|
304 |
- @if(issue.voters.size > 0) { |
|
305 |
- <strong class="count">@issue.voters.size</strong> |
|
306 |
- } |
|
307 |
- </a> |
|
308 |
- } else { |
|
309 |
- <span class="ybtn ybtn-disabled" style="color:#777;" data-toggle="tooltip" title="@Messages("user.login.alert")" data-login="required"> |
|
310 |
- <span class="heart"><i class="yobicon-hearts"></i></span> |
|
311 |
- <span class="desc">@Messages("issue.vote")</span> |
|
312 |
- @if(issue.voters.size > 0) { |
|
313 |
- <strong class="count">@issue.voters.size</strong> |
|
314 |
- } |
|
315 |
- </span> |
|
316 |
- } |
|
317 |
- |
|
318 |
- @if(issue.voters.size > 0){ |
|
319 |
- @partial_voters(issue, 3) |
|
320 |
- } |
|
321 |
- </div> |
|
322 |
- </div> |
|
323 |
- </div> |
|
324 |
- |
|
325 |
- @if(issue.canBeDeleted) { |
|
326 |
- @if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.DELETE)) { |
|
327 |
- <a href="#deleteConfirm" data-toggle="modal" class="ybtn ybtn-danger">@Messages("button.delete")</a> |
|
328 |
- } |
|
329 |
- } else { |
|
330 |
- <button class="ybtn ybtn-disabled" data-toggle="tooltip" data-placement="top" title="@Messages("issue.can.not.be.deleted")">@Messages("button.delete")</button> |
|
331 |
- } |
|
332 |
- |
|
333 |
- @if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.UPDATE)) { |
|
334 |
- <a href="@routes.IssueApp.editIssueForm(project.owner, project.name, issue.getNumber)" class="ybtn">@Messages("button.edit")</a> |
|
335 |
- } |
|
300 |
+ <div class="board-footer"> |
|
301 |
+ <a href="@urlToIssues" class="ybtn pull-left" data-button="historyBack">@Messages("button.list")</a> |
|
302 |
+ @help.keymap("issueDetail", project) |
|
336 | 303 |
</div> |
337 |
- |
|
338 |
- @** Comment **@ |
|
339 |
- <div id="comments" class="board-comment-wrap"> |
|
340 |
- <div id="timeline"> |
|
341 |
- <div class="timeline-list"> |
|
342 |
- @partial_comments(project, issue) |
|
343 |
- </div> |
|
344 |
- </div> |
|
345 |
- @common.commentForm(issue.asResource(), ResourceType.ISSUE_COMMENT, routes.IssueApp.newComment(project.owner, project.name, issue.getNumber).toString()) |
|
346 |
- </div> |
|
347 |
- @** // Comment **@ |
|
348 |
- |
|
349 |
- <a href="@urlToIssues" class="ybtn pull-left" data-button="historyBack">@Messages("button.list")</a> |
|
350 |
- |
|
351 |
- @help.keymap("issueDetail", project) |
|
352 | 304 |
</div> |
353 | 305 |
|
354 | 306 |
<script type="text/x-jquery-tmpl" id="tplAttachedFile"><!-- |
... | ... | @@ -383,22 +335,18 @@ |
383 | 335 |
<script type="text/javascript" src="@routes.Assets.at("javascripts/lib/atjs/jquery.caret.min.js")"></script> |
384 | 336 |
<script type="text/javascript" src="@routes.Assets.at("javascripts/lib/atjs/jquery.atwho.js")"></script> |
385 | 337 |
<script type="text/javascript"> |
386 |
- $(document).ready(function(){ |
|
338 |
+ $(function(){ |
|
387 | 339 |
// yobi.issue.View |
388 | 340 |
$yobi.loadModule("issue.View", { |
389 |
- "sWatchUrl" : "@routes.WatchApp.watch(issue.asResource.asParameter)", |
|
390 |
- "sUnwatchUrl" : "@routes.WatchApp.unwatch(issue.asResource.asParameter)", |
|
391 |
- "sIssuesUrl" : "@routes.IssueApp.massUpdate(project.owner, project.name)", |
|
392 |
- "sTimelineUrl" : "@routes.IssueApp.timeline(project.owner, project.name, issue.getNumber)", |
|
393 |
- "sIssueId" : "@issue.id", |
|
394 |
- "welMilestone" : $("#milestone"), |
|
395 |
- "welAssignee" : $("#assignee"), |
|
396 |
- "welIssueUpdateForm": $("#issueUpdateForm"), |
|
397 |
- "sIssueCheckBoxesSelector": "[type=checkbox][name=checked-issue]", |
|
398 |
- "sNextState" : "@issue.nextState().toString.toLowerCase", |
|
399 |
- "sNextStateUrl" : "@routes.IssueApp.nextState(project.owner, project.name, issue.getNumber)", |
|
400 |
- "sCommentWithStateUrl": "@routes.IssueApp.newComment(project.owner, project.name, issue.getNumber)", |
|
401 |
- "welDueDate": $("#issueDueDate") |
|
341 |
+ "issueId" : "@issue.id", |
|
342 |
+ "nextState": "@issue.nextState().toString.toLowerCase", |
|
343 |
+ "urls" : { |
|
344 |
+ "watch" : "@routes.WatchApp.watch(issue.asResource.asParameter)", |
|
345 |
+ "unwatch" : "@routes.WatchApp.unwatch(issue.asResource.asParameter)", |
|
346 |
+ "timeline" : "@routes.IssueApp.timeline(project.owner, project.name, issue.getNumber)", |
|
347 |
+ "nextState" : "@routes.IssueApp.nextState(project.owner, project.name, issue.getNumber)", |
|
348 |
+ "massUpdate": "@routes.IssueApp.massUpdate(project.owner, project.name)" |
|
349 |
+ } |
|
402 | 350 |
}); |
403 | 351 |
|
404 | 352 |
// yobi.ShortcutKey |
... | ... | @@ -412,9 +360,10 @@ |
412 | 360 |
} |
413 | 361 |
}); |
414 | 362 |
|
363 |
+ // yobi.Mention |
|
415 | 364 |
yobi.Mention({ |
416 |
- target:'textarea[id^=editor-]', |
|
417 |
- url : "@Html(routes.ProjectApp.mentionList(project.owner, project.name, issue.getNumber, issue.asResource().getType.resource()).toString())" |
|
365 |
+ "target": 'textarea[id^=editor-]', |
|
366 |
+ "url" : "@Html(routes.ProjectApp.mentionList(project.owner, project.name, issue.getNumber, issue.asResource().getType.resource()).toString())" |
|
418 | 367 |
}); |
419 | 368 |
}); |
420 | 369 |
</script> |
--- app/views/issue/labels.scala.html
+++ app/views/project/issuelabels.scala.html
... | ... | @@ -23,10 +23,12 @@ |
23 | 23 |
@import utils.TemplateHelper._ |
24 | 24 |
@import utils.AccessControl._ |
25 | 25 |
|
26 |
-@projectLayout("label", project, utils.MenuType.ISSUE){ |
|
27 |
- @projectMenu(project, utils.MenuType.ISSUE, "main-menu-only") |
|
26 |
+@projectLayout("label", project, utils.MenuType.PROJECT_SETTING){ |
|
27 |
+ @projectMenu(project, utils.MenuType.PROJECT_SETTING, "") |
|
28 | 28 |
<div class="page-wrap-outer"> |
29 | 29 |
<div class="project-page-wrap label-editor-wrap"> |
30 |
+ @partial_settingmenu(project) |
|
31 |
+ |
|
30 | 32 |
@if(isProjectResourceCreatable(UserApp.currentUser, project, ResourceType.ISSUE_LABEL)){ |
31 | 33 |
<form id="frmNewLabel" action="@routes.IssueLabelApp.newLabel(project.owner, project.name)" method="post" class="new-label-wrap"> |
32 | 34 |
<strong class="form-legend">@Messages("label.new")</strong> |
... | ... | @@ -56,13 +58,13 @@ |
56 | 58 |
} |
57 | 59 |
|
58 | 60 |
<div id="labelsList" class="issue-label-list-wrap"> |
59 |
- @partial_labels_list(project, labels) |
|
61 |
+ @partial_issuelabels_list(project, labels) |
|
60 | 62 |
</div> |
61 | 63 |
</div> |
62 | 64 |
</div> |
63 | 65 |
|
64 |
- @partial_labels_editcategory() |
|
65 |
- @partial_labels_editlabel(project) |
|
66 |
+ @partial_issuelabels_editcategory() |
|
67 |
+ @partial_issuelabels_editlabel(project) |
|
66 | 68 |
@common.select2() |
67 | 69 |
|
68 | 70 |
<script type="text/javascript"> |
--- app/views/issue/partial_labels_editcategory.scala.html
+++ app/views/project/partial_issuelabels_editcategory.scala.html
No changes |
--- app/views/issue/partial_labels_editlabel.scala.html
+++ app/views/project/partial_issuelabels_editlabel.scala.html
No changes |
--- app/views/issue/partial_labels_list.scala.html
+++ app/views/project/partial_issuelabels_list.scala.html
... | ... | @@ -40,7 +40,7 @@ |
40 | 40 |
|
41 | 41 |
@labels.groupBy(_.category).map { |
42 | 42 |
case (category, labelsInCategory) => { |
43 |
- <div class="row-fluid list-item" data-category="@category.id" data-category-name="@category.name"> |
|
43 |
+ <div class="row-fluid list-item category-wrap" data-category="@category.id" data-category-name="@category.name"> |
|
44 | 44 |
<div class="span3"> |
45 | 45 |
<h5 class="right-txt mr20"> |
46 | 46 |
<span class="category-name"> |
... | ... | @@ -81,7 +81,8 @@ |
81 | 81 |
<td class="actions"> |
82 | 82 |
@if(isAllowed(UserApp.currentUser, label.asResource(), Operation.DELETE)) { |
83 | 83 |
<button type="button" class="ybtn ybtn-danger ybtn-small" |
84 |
- data-category="@category.id" data-label-id="@label.id" |
|
84 |
+ data-category-name="@category.name" |
|
85 |
+ data-label-id="@label.id" |
|
85 | 86 |
data-delete-uri="@routes.IssueLabelApp.delete(project.owner, project.name, label.id)"> |
86 | 87 |
@Messages("button.delete") |
87 | 88 |
</button> |
... | ... | @@ -92,7 +93,7 @@ |
92 | 93 |
data-category-id="@category.id" |
93 | 94 |
data-label-name="@label.name" |
94 | 95 |
data-label-color="@label.color" |
95 |
- data-label-update-uri="@routes.IssueLabelApp.update(project.owner, project.name, label.id)"> |
|
96 |
+ data-update-uri="@routes.IssueLabelApp.update(project.owner, project.name, label.id)"> |
|
96 | 97 |
@Messages("button.edit") |
97 | 98 |
</button> |
98 | 99 |
} |
--- conf/messages
+++ conf/messages
... | ... | @@ -231,6 +231,7 @@ |
231 | 231 |
issue.event.referred.title = mentioned |
232 | 232 |
issue.event.unassigned = {0} set assignee to unassigned |
233 | 233 |
issue.is.empty = No issue found |
234 |
+issue.label = Issue Label |
|
234 | 235 |
issue.list.all = All issues |
235 | 236 |
issue.list.assignedToMe = Assigned to me |
236 | 237 |
issue.list.authoredByMe = Created by me |
... | ... | @@ -251,12 +252,12 @@ |
251 | 252 |
issue.state.open = In progress |
252 | 253 |
issue.unvote.description = Click here if you no longer sympathize with this issue. |
253 | 254 |
issue.unwatch.start = You will no longer get notifications about this issue |
254 |
-issue.update.assignee = Update assignee |
|
255 |
+issue.update.assignee.id = Update assignee |
|
255 | 256 |
issue.update.attachLabel = Attach label |
256 | 257 |
issue.update.detachLabel = Detach label |
257 |
-issue.update.duedate = Update due date |
|
258 |
-issue.update.label = Update label |
|
259 |
-issue.update.milestone = Update milestone |
|
258 |
+issue.update.dueDate = Update due date |
|
259 |
+issue.update.labelIds = Update label |
|
260 |
+issue.update.milestone.id = Update milestone |
|
260 | 261 |
issue.update.state = Update status |
261 | 262 |
issue.vote = Agree |
262 | 263 |
issue.vote.description = Click here if you sympathize with this issue. |
--- conf/messages.ja
+++ conf/messages.ja
... | ... | @@ -205,10 +205,11 @@ |
205 | 205 |
issue.state.finished = 完了 |
206 | 206 |
issue.state.open = オープン |
207 | 207 |
issue.unwatch.start = 今後このイシューに関するお知らせを受けません |
208 |
-issue.update.assignee = 担当者変更 |
|
208 |
+issue.update.assignee.id = 担当者変更 |
|
209 | 209 |
issue.update.attachLabel = ラベル追加 |
210 | 210 |
issue.update.detachLabel = ラベル消去 |
211 |
-issue.update.milestone = マイルストーン変更 |
|
211 |
+issue.update.labelIds = ラベル変更 |
|
212 |
+issue.update.milestone.id = マイルストーン変更 |
|
212 | 213 |
issue.update.state = 状態変更 |
213 | 214 |
issue.watch.start = 今後このイシューに関するお知らせを受けることになります |
214 | 215 |
label = ラベル |
--- conf/messages.ko
+++ conf/messages.ko
... | ... | @@ -164,6 +164,7 @@ |
164 | 164 |
common.time.second = {0}초 전 |
165 | 165 |
common.time.seconds = {0}초 전 |
166 | 166 |
common.time.today = 오늘 |
167 |
+common.unselect = 선택안함 |
|
167 | 168 |
emails.click.link = 이메일을 확인하려면 다음 링크를 클릭하세요. |
168 | 169 |
emails.main.email = 대표 이메일 |
169 | 170 |
emails.main.email.descr = 대표 이메일로 설정한 이메일로 알림을 받거나 비밀번호 변경 요청을 받을 수 있습니다. |
... | ... | @@ -231,6 +232,7 @@ |
231 | 232 |
issue.event.referred.title = 언급됨 |
232 | 233 |
issue.event.unassigned = {0}님이 이 이슈의 담당자를 "없음"으로 설정하였습니다. |
233 | 234 |
issue.is.empty = 등록된 이슈가 없습니다. |
235 |
+issue.label = 이슈 라벨 |
|
234 | 236 |
issue.list.all = 전체 이슈 |
235 | 237 |
issue.list.assignedToMe = 나에게 할당된 이슈 |
236 | 238 |
issue.list.authoredByMe = 내가 작성한 이슈 |
... | ... | @@ -251,12 +253,12 @@ |
251 | 253 |
issue.state.open = 열림 |
252 | 254 |
issue.unvote.description = 공감을 취소하려면 버튼을 누릅니다. |
253 | 255 |
issue.unwatch.start = 이제 이 이슈에 관한 알림을 받지 않습니다 |
254 |
-issue.update.assignee = 담당자 변경 |
|
256 |
+issue.update.assignee.id = 담당자 변경 |
|
255 | 257 |
issue.update.attachLabel = 라벨 추가 |
256 | 258 |
issue.update.detachLabel = 라벨 제거 |
257 |
-issue.update.duedate = 목표완료일 변경 |
|
258 |
-issue.update.label = 라벨 변경 |
|
259 |
-issue.update.milestone = 마일스톤 변경 |
|
259 |
+issue.update.dueDate = 목표완료일 변경 |
|
260 |
+issue.update.labelIds = 라벨 변경 |
|
261 |
+issue.update.milestone.id = 마일스톤 변경 |
|
260 | 262 |
issue.update.state = 상태 변경 |
261 | 263 |
issue.vote = 공감 |
262 | 264 |
issue.vote.description = 이 이슈에 공감하기 위해 버튼을 누릅니다. |
--- public/javascripts/common/yobi.Label.js
... | ... | @@ -1,382 +0,0 @@ |
1 | -/** | |
2 | - * Yobi, Project Hosting SW | |
3 | - * | |
4 | - * Copyright 2013 NAVER Corp. | |
5 | - * http://yobi.io | |
6 | - * | |
7 | - * @Author Jihan Kim | |
8 | - * | |
9 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
10 | - * you may not use this file except in compliance with the License. | |
11 | - * You may obtain a copy of the License at | |
12 | - * | |
13 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
14 | - * | |
15 | - * Unless required by applicable law or agreed to in writing, software | |
16 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
17 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
18 | - * See the License for the specific language governing permissions and | |
19 | - * limitations under the License. | |
20 | - */ | |
21 | -/** | |
22 | - * yobi.Label | |
23 | - * 라벨 목록을 가져다 표현해주는 역할 | |
24 | - * (개별 삭제 링크 처리 포함) | |
25 | - */ | |
26 | -yobi.Label = (function(htOptions){ | |
27 | - | |
28 | - var htVar = {}; | |
29 | - var htElement = {}; | |
30 | - | |
31 | - /** | |
32 | - * initialize | |
33 | - * @param {Hash Table} htOptions | |
34 | - * @param {Boolean} htOptions.bEditable | |
35 | - * @param {String} htOptions.sTplLabel | |
36 | - * @param {String} htOptions.sTplControls | |
37 | - * @param {String} htOptions.sTplBtnLabelId | |
38 | - * @param {String} htOptions.sURLLabels | |
39 | - * @param {String} htOptions.sURLPost | |
40 | - */ | |
41 | - function _init(htOptions){ | |
42 | - htOptions = $.extend({"bEditable": false}, htOptions); | |
43 | - _initVar(htOptions); | |
44 | - _initElement(htOptions); | |
45 | - _initLabelEditor(); | |
46 | - _attachEvent(); | |
47 | - } | |
48 | - | |
49 | - /** | |
50 | - * initialize variable except element | |
51 | - * htVar.sURLLabels = htOptions.sURLLabels; | |
52 | - * htVar.sURLPost = htOptions.sURLPost; | |
53 | - * htVar.bEditable = htOptions.bEditable; | |
54 | - * @param {Hash Table} htOptions | |
55 | - */ | |
56 | - function _initVar(htOptions){ | |
57 | - // copy htOptions to htVar | |
58 | - for(key in htOptions){ | |
59 | - htVar[key] = htOptions[key]; | |
60 | - } | |
61 | - | |
62 | - htVar.sURLLabel = htVar.sURLLabels.substr(0, htVar.sURLLabels.length-1); | |
63 | - | |
64 | - htVar.sTplLabel = htOptions.sTplLabel || '<div class="control-group"><label class="control-label" data-category="${category}">${category}</label></div>'; | |
65 | - htVar.sTplControls = htOptions.sTplControls || '<div class="controls label-group" data-category="${category}"></div>'; | |
66 | - htVar.sTplBtnLabelId = htOptions.sTplBtnLabelId || '<span class="issue-label ${labelCSS} ${activeClass}" data-labelId="${labelId}">${labelName}${deleteButton}</span>'; | |
67 | - htVar.sTplLabelItem = $('#labelListItem').text(); | |
68 | - htVar.sTplLabelCategoryItem = $('#labelCatetoryItem').text(); | |
69 | - } | |
70 | - | |
71 | - /** | |
72 | - * initialize element variable | |
73 | - */ | |
74 | - function _initElement(htOptions){ | |
75 | - htElement.welContainer = $(htOptions.welContainer || "fieldset.labels"); | |
76 | - htElement.welForm = $(htOptions.welForm || 'form#issue-form,form.form-search,form#search'); | |
77 | - | |
78 | - // add label | |
79 | - htElement.welLabels = $('.labels'); | |
80 | - htElement.welLabelEditor = $('.label-editor'); | |
81 | - | |
82 | - htElement.welBtnManageLabel = $(htOptions.welBtnManageLabel || "#manage-label-link"); | |
83 | - | |
84 | - // setting label list | |
85 | - htElement.welAttachLabels = $('#attach-label-list'); | |
86 | - htElement.welDeleteLabels = $('#delete-label-list'); | |
87 | - } | |
88 | - | |
89 | - /** | |
90 | - * initialize event handler | |
91 | - */ | |
92 | - function _attachEvent(){ | |
93 | - htElement.welForm.submit(_onSubmitForm); | |
94 | - htElement.welBtnManageLabel.click(_clickBtnManageLabel); | |
95 | - } | |
96 | - | |
97 | - function _onSubmitForm(){ | |
98 | - // append labelIds to searchForm | |
99 | - if(htVar.bEditable === false){ | |
100 | - _appendSelectedLabelIdsToForm(); | |
101 | - } | |
102 | - | |
103 | - return true; | |
104 | - } | |
105 | - | |
106 | - /** | |
107 | - * @private | |
108 | - */ | |
109 | - function _appendSelectedLabelIdsToForm(){ | |
110 | - // clear former fields first | |
111 | - _clearLabelIdsOnForm(); | |
112 | - | |
113 | - var aValues = []; | |
114 | - var waSelectedLabels = $("fieldset.labels div[data-category] span.active[data-labelId]"); | |
115 | - | |
116 | - waSelectedLabels.each(function(i, elLabel){ | |
117 | - aValues.push('<input type="hidden" name="labelIds" value="'+ $(elLabel).attr('data-labelId') + '">'); | |
118 | - }); | |
119 | - | |
120 | - htElement.welForm.append(aValues); | |
121 | - waSelectedLabels = null; | |
122 | - } | |
123 | - | |
124 | - /** | |
125 | - * @private | |
126 | - */ | |
127 | - function _clearLabelIdsOnForm(){ | |
128 | - $("input[name='labelIds']").each(function(i, elInput) { | |
129 | - $(elInput).remove(); | |
130 | - }); | |
131 | - } | |
132 | - | |
133 | - function _clickBtnManageLabel() { | |
134 | - if(htVar.bEditable === false){ | |
135 | - _appendSelectedLabelIdsToForm(); | |
136 | - } | |
137 | - | |
138 | - htVar.bEditable = !htVar.bEditable; | |
139 | - _initLabelEditor(); | |
140 | - } | |
141 | - | |
142 | - /** | |
143 | - * initialize Label Editor | |
144 | - */ | |
145 | - function _initLabelEditor(){ | |
146 | - htElement.welContainer.empty(); | |
147 | - | |
148 | - if(htVar.bEditable){ | |
149 | - yobi.LabelEditor.appendTo(htElement.welContainer, { | |
150 | - "sURLPost" : htVar.sURLPost, | |
151 | - "fOnCreate": _onCreateNewLabel | |
152 | - }); | |
153 | - } | |
154 | - | |
155 | - _getLabels(htVar.fOnLoad); | |
156 | - } | |
157 | - | |
158 | - /** | |
159 | - * @param {Object} oLabel | |
160 | - */ | |
161 | - function _onCreateNewLabel(oLabel){ | |
162 | - _addLabelIntoCategory(oLabel); | |
163 | - _setActiveLabel(oLabel.id, oLabel.color); | |
164 | - _addLabelForSettingGroup(oLabel); | |
165 | - $('input[name="labelName"]').val(""); | |
166 | - } | |
167 | - | |
168 | - function _addLabelForSettingGroup(oLabel) { | |
169 | - var sLabel; | |
170 | - var welAttachDivider = htElement.welAttachLabels.find('li[data-category="'+oLabel.category+'"].divider'); | |
171 | - var welDeleteDivider = htElement.welDeleteLabels.find('li[data-category="'+oLabel.category+'"].divider'); | |
172 | - | |
173 | - if(welAttachDivider.length === 0) { | |
174 | - sLabel = $yobi.tmpl(htVar.sTplLabelCategoryItem, oLabel); | |
175 | - htElement.welAttachLabels.prepend(sLabel); | |
176 | - htElement.welDeleteLabels.prepend(sLabel); | |
177 | - } else { | |
178 | - sLabel = $yobi.tmpl(htVar.sTplLabelItem, oLabel); | |
179 | - welAttachDivider.before(sLabel); | |
180 | - welDeleteDivider.before(sLabel); | |
181 | - } | |
182 | - } | |
183 | - | |
184 | - /** | |
185 | - * @param {Function} fCallback | |
186 | - */ | |
187 | - function _getLabels(fCallback){ | |
188 | - // send request | |
189 | - $.get(htVar.sURLLabels, function(oRes){ | |
190 | - if(!(oRes instanceof Object)){ | |
191 | - return; | |
192 | - } | |
193 | - | |
194 | - // add label into category after sort | |
195 | - var aLabels = oRes.sort(function(a, b) { | |
196 | - return (a.category == b.category) ? (a.name > b.name) : (a.category > b.category); | |
197 | - }); | |
198 | - $(aLabels).each(function(nIndex, oLabel){ | |
199 | - _addLabelIntoCategory(oLabel); | |
200 | - }); | |
201 | - | |
202 | - // run callback function | |
203 | - if (typeof fCallback == "function") { | |
204 | - fCallback(this); | |
205 | - } | |
206 | - | |
207 | - aLabels = null; | |
208 | - }); | |
209 | - } | |
210 | - | |
211 | - /** | |
212 | - * add label into category | |
213 | - * @param {Number} oLabel.id | |
214 | - * @param {String} oLabel.category | |
215 | - * @param {String} oLabel.name | |
216 | - * @param {String} oLabel.color | |
217 | - * @return {Wrapped Element} | |
218 | - */ | |
219 | - function _addLabelIntoCategory(oLabel) { | |
220 | - // set Label Color | |
221 | - _setLabelColor(oLabel); | |
222 | - | |
223 | - // label Id | |
224 | - var welBtnLabelId = $($yobi.tmpl(htVar.sTplBtnLabelId, { | |
225 | - "labelId" : oLabel.id, | |
226 | - "labelName" : oLabel.name, | |
227 | - "labelCSS" : 'active-' + $yobi.getContrastColor(oLabel.color), | |
228 | - "activeClass" : _getActiveClass(parseInt(oLabel.id)), | |
229 | - "deleteButton": htVar.bEditable ? '<span class="delete">×</span>' : '' | |
230 | - })); | |
231 | - | |
232 | - if(htVar.bEditable){ | |
233 | - welBtnLabelId.addClass('active'); | |
234 | - } | |
235 | - welBtnLabelId.click(_onClickLabel); | |
236 | - | |
237 | - var welCategory = $('fieldset.labels div[data-category="' + oLabel.category + '"]'); | |
238 | - if (welCategory.length > 0) { | |
239 | - welCategory.append(welBtnLabelId); | |
240 | - return welBtnLabelId; | |
241 | - } | |
242 | - | |
243 | - var welLabel = $.tmpl(htVar.sTplLabel, {"category": oLabel.category}); | |
244 | - var welControls = $.tmpl(htVar.sTplControls, {"category": oLabel.category}); | |
245 | - welControls.append(welBtnLabelId); // Edit Button | |
246 | - welLabel.append(welControls); // Controls | |
247 | - | |
248 | - if(htVar.bEditable){ | |
249 | - yobi.LabelEditor.addCategory(oLabel.category); | |
250 | - } | |
251 | - | |
252 | - // add label into category | |
253 | - if(htElement.welLabelEditor.length > 0) { | |
254 | - htElement.welLabelEditor.before(welLabel); | |
255 | - } else { | |
256 | - htElement.welLabels.prepend(welLabel); | |
257 | - } | |
258 | - | |
259 | - return welBtnLabelId; | |
260 | - } | |
261 | - | |
262 | - /** | |
263 | - * @param {Object} oLabel | |
264 | - */ | |
265 | - function _setLabelColor(oLabel){ | |
266 | - var sDefaultCSSTarget = '.issue-label[data-labelId="' + oLabel.id + '"]'; | |
267 | - var sActiveCSSTarget = '.issue-label.active[data-labelId="' + oLabel.id + '"]'; | |
268 | - | |
269 | - var aDefaultCss = []; | |
270 | - var sDefaultCssSkel = 'box-shadow: inset 2px 0 0px ' + oLabel.color; | |
271 | - ["", "-moz-", "-webkit-"].forEach(function(sPrefix){ | |
272 | - aDefaultCss.push(sPrefix + sDefaultCssSkel); | |
273 | - }); | |
274 | - var sDefaultCss = aDefaultCss.join(";"); | |
275 | - var sActiveCss = 'background-color: ' + oLabel.color + '; color:'+$yobi.getContrastColor(oLabel.color); | |
276 | - | |
277 | - if(document.styleSheets[0].addRule) { | |
278 | - document.styleSheets[0].addRule(sActiveCSSTarget,sActiveCss); | |
279 | - document.styleSheets[0].addRule(sDefaultCSSTarget,sDefaultCss); | |
280 | - } else { | |
281 | - document.styleSheets[0].insertRule(sActiveCSSTarget+'{'+ sActiveCss +'}',0); | |
282 | - document.styleSheets[0].insertRule(sDefaultCSSTarget+'{'+ sDefaultCss +'}',0); | |
283 | - } | |
284 | - } | |
285 | - | |
286 | - /** | |
287 | - * @param {Number} nLabelId | |
288 | - */ | |
289 | - function _getActiveClass(nLabelId) { | |
290 | - if (htVar.aSelectedLabels && htVar.aSelectedLabels.indexOf(nLabelId) != -1) { | |
291 | - return 'active'; | |
292 | - } | |
293 | - return ''; | |
294 | - } | |
295 | - | |
296 | - /** | |
297 | - * @param {Wrapped Event} weEvt | |
298 | - * @return {Boolean} false | |
299 | - */ | |
300 | - function _onClickLabel(weEvt){ | |
301 | - var welCurrent = $(weEvt.target); // SPAN .delete or .issue-label | |
302 | - var welLabel = welCurrent.attr("data-labelId") ? welCurrent : welCurrent.parent("[data-labelId]"); | |
303 | - var sLabelId = welLabel.attr("data-labelId"); | |
304 | - | |
305 | - if(htVar.bEditable && welCurrent.hasClass("delete")){ | |
306 | - if(confirm(Messages("label.confirm.delete")) === false){ | |
307 | - return false; | |
308 | - } | |
309 | - | |
310 | - return _requestDeleteLabel(sLabelId); | |
311 | - } | |
312 | - | |
313 | - if(!htVar.bEditable){ | |
314 | - welLabel.siblings().removeClass("active"); | |
315 | - welLabel.toggleClass("active"); | |
316 | - } | |
317 | - | |
318 | - if (htVar.bRefresh) { | |
319 | - htElement.welForm.submit(); | |
320 | - } | |
321 | - | |
322 | - return false; | |
323 | - } | |
324 | - | |
325 | - | |
326 | - /** | |
327 | - * request to delete label | |
328 | - * @param sLabelId | |
329 | - * @private | |
330 | - */ | |
331 | - function _requestDeleteLabel(sLabelId){ | |
332 | - if(!sLabelId){ | |
333 | - return false; | |
334 | - } | |
335 | - | |
336 | - $.post( | |
337 | - htVar.sURLLabel + '/' + sLabelId + '/delete', | |
338 | - {"_method": "delete"} | |
339 | - ).done(function(){ | |
340 | - _removeLabel(sLabelId); | |
341 | - }); | |
342 | - | |
343 | - return true; | |
344 | - } | |
345 | - | |
346 | - /** | |
347 | - * remove label on list | |
348 | - * @param {String} sLabelId | |
349 | - */ | |
350 | - function _removeLabel(sLabelId) { | |
351 | - var welEditorLabel = $('.issue-form-labels').find('[data-labelId=' + sLabelId + ']'); | |
352 | - var welSettingLabel = htElement.welAttachLabels.find('li[data-value="'+sLabelId+'"]'); | |
353 | - | |
354 | - if(welEditorLabel.siblings().length ===0) { | |
355 | - var sCategory = welSettingLabel.data('category'); | |
356 | - welEditorLabel.parents('div.control-group').remove(); | |
357 | - htElement.welAttachLabels.find('li[data-category="'+sCategory+'"]').remove(); | |
358 | - yobi.LabelEditor.removeCategory(sCategory); | |
359 | - } else { | |
360 | - welEditorLabel.remove(); | |
361 | - welSettingLabel.remove(); | |
362 | - } | |
363 | - } | |
364 | - | |
365 | - /** | |
366 | - * @param {String} sId | |
367 | - */ | |
368 | - function _setActiveLabel(sId){ | |
369 | - $('.labels .issue-label[data-labelId="' + sId + '"]').addClass('active'); | |
370 | - } | |
371 | - | |
372 | - function _resetLabel(labelId) { | |
373 | - $(".labels .issue-label").removeClass("active"); | |
374 | - _setActiveLabel(labelId); | |
375 | - } | |
376 | - | |
377 | - return { | |
378 | - "init": _init, | |
379 | - "setActiveLabel": _setActiveLabel, | |
380 | - "resetLabel": _resetLabel | |
381 | - }; | |
382 | -})(); |
--- public/javascripts/common/yobi.LabelEditor.js
... | ... | @@ -1,326 +0,0 @@ |
1 | -/** | |
2 | - * Yobi, Project Hosting SW | |
3 | - * | |
4 | - * Copyright 2013 NAVER Corp. | |
5 | - * http://yobi.io | |
6 | - * | |
7 | - * @Author Jihan Kim | |
8 | - * | |
9 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
10 | - * you may not use this file except in compliance with the License. | |
11 | - * You may obtain a copy of the License at | |
12 | - * | |
13 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
14 | - * | |
15 | - * Unless required by applicable law or agreed to in writing, software | |
16 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
17 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
18 | - * See the License for the specific language governing permissions and | |
19 | - * limitations under the License. | |
20 | - */ | |
21 | -/** | |
22 | - * yobi.LabelEditor | |
23 | - * 새 라벨 추가를 위한 에디터 인터페이스 | |
24 | - */ | |
25 | -yobi.LabelEditor = (function(welContainer, htOptions){ | |
26 | - | |
27 | - var htVar = {}; | |
28 | - var htElement = {}; | |
29 | - | |
30 | - /** | |
31 | - * initialize | |
32 | - * @param {Wrapped Element} welContainer Container element to append label editor | |
33 | - * @param {Hash Table} htOptions | |
34 | - */ | |
35 | - function _init(welContainer, htOptions){ | |
36 | - _initVar(htOptions); | |
37 | - _initElement(welContainer); | |
38 | - _attachEvent(); | |
39 | - } | |
40 | - | |
41 | - /** | |
42 | - * initialize variables | |
43 | - * @param {Hash Table} htOptions | |
44 | - * @param {Function} htOptions.fOnCreate | |
45 | - * @param {String} htOptions.sURLPost | |
46 | - * @param {String} htOptions.sTplEditor | |
47 | - * @param {String} htOptions.sTplBtnColor | |
48 | - * @param {Array} htOptions.aColors | |
49 | - */ | |
50 | - function _initVar(htOptions){ | |
51 | - htVar.sURLPost = htOptions.sURLPost; | |
52 | - htVar.fOnCreate = htOptions.fOnCreate; | |
53 | - | |
54 | - htVar.aColors = htOptions.aColors || ['#da5454','#f86ca0','#ff9e9d','#ffcc33','#f8c86c','#ff9933','#99ca3c','#3fb8af','#22b4b9','#6ca6f8','#4d68b1','#9966cc']; | |
55 | - htVar.sTplEditor = htOptions.sTplEditor || $("#tplYobiLabelEditor").text(); | |
56 | - htVar.sTplBtnColor = htOptions.sTplBtnColor || '<button type="button" class="issue-label issueColor nbtn square" style="background-color:${color}">'; | |
57 | - } | |
58 | - | |
59 | - /** | |
60 | - * initialize elements | |
61 | - * @param {Wrapped Element} welContainer | |
62 | - */ | |
63 | - function _initElement(welContainer){ | |
64 | - htElement.welContainer = $(welContainer); | |
65 | - htElement.welEditor = _getLabelEditor(); | |
66 | - htElement.welContainer.append(htElement.welEditor); | |
67 | - | |
68 | - htElement.welWrap = $("#custom-label"); | |
69 | - htElement.welColors = htElement.welWrap.find("div.colors"); | |
70 | - _makeColorTable(); // 색상표 생성 | |
71 | - | |
72 | - htElement.waBtnCustomColor = htElement.welWrap.find("button.issueColor"); | |
73 | - htElement.welCustomLabelColor = htElement.welWrap.find("input[name=labelColor]"); // $('#custom-label-color'); | |
74 | - htElement.welCustomLabelName = htElement.welWrap.find("input[name=labelName]"); // $('#custom-label-name'); | |
75 | - htElement.welCustomLabelCategory = htElement.welWrap.find("input[name=labelCategory]"); // $('#custom-label-category'); | |
76 | - htElement.welCustomLabelCategory.typeahead(); | |
77 | - htElement.welBtnCustomLabelSubmit = htElement.welWrap.find("button.labelSubmit"); //$('#custom-label-submit'); | |
78 | - | |
79 | - // Focus to the category input area. | |
80 | - htElement.welCustomLabelCategory.focus(); | |
81 | - } | |
82 | - | |
83 | - /** | |
84 | - * @returns {Boolean} false on enter key has pressed | |
85 | - */ | |
86 | - function _preventDefaultWhenEnterPressed(weEvt) { | |
87 | - return !((weEvt.keyCode || weEvt.which) === 13); | |
88 | - } | |
89 | - | |
90 | - /** | |
91 | - * @private false when enter key pressed | |
92 | - */ | |
93 | - function _preventSubmitAndMoveWhenEnterPressed(eEvt, welTarget){ | |
94 | - var code = eEvt.keyCode || eEvt.which; | |
95 | - if(code === 13){ | |
96 | - welTarget.focus(); | |
97 | - eEvt.preventDefault(); | |
98 | - } | |
99 | - } | |
100 | - | |
101 | - /** | |
102 | - * attach events | |
103 | - */ | |
104 | - function _attachEvent(){ | |
105 | - htElement.waBtnCustomColor.click(_onClickBtnCustomColor); | |
106 | - htElement.welBtnCustomLabelSubmit.click(_onClickBtnSubmitCustom); | |
107 | - | |
108 | - htElement.welCustomLabelCategory | |
109 | - .keypress(_preventDefaultWhenEnterPressed); | |
110 | - htElement.welCustomLabelName | |
111 | - .focus(_onFocusLabelName) | |
112 | - .keypress(_preventDefaultWhenEnterPressed) | |
113 | - .keyup(function(weEvt) { | |
114 | - if((weEvt.keyCode || weEvt.which) === 13){ | |
115 | - htElement.welCustomLabelColor.focus(); | |
116 | - weEvt.preventDefault(); | |
117 | - return false; | |
118 | - } | |
119 | - }); | |
120 | - htElement.welCustomLabelColor | |
121 | - .keypress(function(weEvt){ | |
122 | - if((weEvt.keyCode || weEvt.which) === 13){ | |
123 | - _addCustomLabel(); | |
124 | - weEvt.preventDefault(); | |
125 | - return false; | |
126 | - } | |
127 | - }) | |
128 | - .keyup(function(weEvt){ | |
129 | - _preventSubmitAndMoveWhenEnterPressed(weEvt, htElement.welCustomLabelName); | |
130 | - }); | |
131 | - htElement.welCustomLabelColor.keyup(_onKeyupInputColorCustom); | |
132 | - } | |
133 | - | |
134 | - function _onFocusLabelName(){ | |
135 | - var sCategory = htElement.welCustomLabelCategory.val(); | |
136 | - var sColor = htElement.welCustomLabelColor.val(); | |
137 | - | |
138 | - if(!sCategory.trim()){ | |
139 | - return; | |
140 | - } | |
141 | - | |
142 | - var welFirstItemInCategory = $('.label-group[data-category="' + sCategory + '"] > .issue-label:first'); | |
143 | - | |
144 | - if(welFirstItemInCategory.length > 0 && !sColor.trim()){ | |
145 | - var sColor = new RGBColor(welFirstItemInCategory.css("background-color")).toHex(); | |
146 | - _updateSelectedColor(sColor); | |
147 | - htElement.welCustomLabelColor.val(sColor); | |
148 | - } | |
149 | - } | |
150 | - | |
151 | - /** | |
152 | - * Get label Editor | |
153 | - * @return {Wrapped Element} | |
154 | - */ | |
155 | - function _getLabelEditor(){ | |
156 | - // label editor HTML | |
157 | - var welEditor = $yobi.tmpl(htVar.sTplEditor, { | |
158 | - "labelAdd" : Messages("label.add"), | |
159 | - "labelNew" : Messages("label.new"), | |
160 | - "labelName" : Messages("label.name"), | |
161 | - "labelCategory" : Messages('label.category'), | |
162 | - "labelCustomColor": Messages("label.customColor") | |
163 | - }); | |
164 | - | |
165 | - return welEditor; | |
166 | - } | |
167 | - | |
168 | - function _makeColorTable(){ | |
169 | - var aColorBtns = []; | |
170 | - htVar.aColors.forEach(function(sColor){ | |
171 | - aColorBtns.push($yobi.tmpl(htVar.sTplBtnColor, {"color": sColor})); | |
172 | - }); | |
173 | - htElement.welColors.prepend(aColorBtns); | |
174 | - aColorBtns = null; | |
175 | - } | |
176 | - | |
177 | - function _onClickBtnSubmitCustom(){ | |
178 | - _addCustomLabel(); | |
179 | - htElement.welCustomLabelName.focus(); | |
180 | - } | |
181 | - | |
182 | - /** | |
183 | - * add custom label | |
184 | - */ | |
185 | - function _addCustomLabel(){ | |
186 | - var htData = { | |
187 | - "name" : htElement.welCustomLabelName.val(), | |
188 | - "color" : htElement.welCustomLabelColor.val(), | |
189 | - "category": htElement.welCustomLabelCategory.val() | |
190 | - }; | |
191 | - | |
192 | - if(htData.name.length === 0 || htData.color.length === 0 || htData.category.length === 0){ | |
193 | - $yobi.alert(Messages("label.error.empty")); | |
194 | - return false; | |
195 | - } | |
196 | - | |
197 | - var sColor = htElement.welCustomLabelColor.val(); | |
198 | - var oColor = new RGBColor(sColor); | |
199 | - | |
200 | - if(!oColor.ok){ | |
201 | - $yobi.alert(Messages("label.error.color", sColor)); | |
202 | - return false; | |
203 | - } | |
204 | - | |
205 | - htElement.welCustomLabelColor.val(oColor.toHex()); | |
206 | - | |
207 | - // send request | |
208 | - $yobi.sendForm({ | |
209 | - "sURL" : htVar.sURLPost, | |
210 | - "htData" : htData, | |
211 | - "htOptForm": {"enctype": "multipart/form-data"}, | |
212 | - "fOnLoad" : function(oRes){ | |
213 | - // label.id, label.category, label.name, label.color | |
214 | - if (!(oRes instanceof Object)) { | |
215 | - var sMessage = Messages("label.error.creationFailed"); | |
216 | - | |
217 | - if($(".labels .issue-label:contains('" + htData.name + "')").length > 0){ | |
218 | - sMessage = Messages("label.error.duplicated"); | |
219 | - } | |
220 | - | |
221 | - $yobi.alert(sMessage); | |
222 | - return; | |
223 | - } | |
224 | - | |
225 | - htElement.welCustomLabelName.val(""); | |
226 | - _addCategoryTypeahead(oRes.category); | |
227 | - | |