[Notice] Announcing the End of Demo Server [Read me]
comment: Support attachments modification
@cca1b798fa5b993207d228bd48206e5b708bb553
--- app/assets/stylesheets/less/_page.less
+++ app/assets/stylesheets/less/_page.less
... | ... | @@ -3308,7 +3308,6 @@ |
3308 | 3308 |
font-family:@base-font-family; |
3309 | 3309 |
|
3310 | 3310 |
.write-comment-wrap { |
3311 |
- margin-bottom: 10px; |
|
3312 | 3311 |
position: relative; |
3313 | 3312 |
.nav { |
3314 | 3313 |
margin-bottom: 0; |
... | ... | @@ -3461,77 +3460,76 @@ |
3461 | 3460 |
margin-top: 15px; |
3462 | 3461 |
border-top: 1px solid #e0e0e0; |
3463 | 3462 |
padding: 15px 0px; |
3464 |
- |
|
3465 |
- .attached-file { |
|
3466 |
- display: inline-block; |
|
3467 |
- width: 355px; |
|
3468 |
- height: 30px; |
|
3469 |
- line-height: 30px; |
|
3470 |
- border: 1px solid #ccc; |
|
3471 |
- background: #fafafa; |
|
3472 |
- padding: 0 10px; |
|
3473 |
- margin-bottom: 5px; |
|
3474 |
- margin-right: 4px; |
|
3475 |
- cursor:pointer; |
|
3476 |
- overflow: hidden; |
|
3477 |
- -webkit-transition-duration: 0.5s; |
|
3478 |
- &:hover { border:1px solid @primary; } |
|
3479 |
- |
|
3480 |
- i { |
|
3481 |
- display:none; |
|
3482 |
- margin-right:3px; |
|
3483 |
- vertical-align: middle; |
|
3484 |
- color:@yobi-blue; |
|
3485 |
- } |
|
3486 |
- .name { |
|
3487 |
- display: inline-block; |
|
3488 |
- max-width: 120px; |
|
3489 |
- overflow: hidden; |
|
3490 |
- text-overflow: ellipsis; |
|
3491 |
- white-space:nowrap; |
|
3492 |
- vertical-align:middle; |
|
3493 |
- -webkit-transition-duration: 0.5s; |
|
3494 |
- } |
|
3495 |
- .size { font-size:11px; vertical-align:middle; } |
|
3496 |
- |
|
3497 |
- /* 업로드 하는 도중에는 진행 상태 표시 */ |
|
3498 |
- .progress { |
|
3499 |
- display: inline-block; |
|
3500 |
- width: 100px; |
|
3501 |
- height: 7px; |
|
3502 |
- margin: 0; |
|
3503 |
- overflow: hidden; |
|
3504 |
- } |
|
3505 |
- .btn-delete { |
|
3506 |
- display:none; |
|
3507 |
- width: 30px; |
|
3508 |
- height: 30px; |
|
3509 |
- font-size: 1.5em; |
|
3510 |
- font-weight: bold; |
|
3511 |
- &:hover { color:@primary; } |
|
3512 |
- } |
|
3513 |
- .btn-insert { |
|
3514 |
- display:none; |
|
3515 |
- line-height: 20px; |
|
3516 |
- margin-top: 2px; margin-right: 10px; |
|
3517 |
- .box-shadow(none); |
|
3518 |
- &:hover { background:#fff; color:@primary; } |
|
3519 |
- } |
|
3520 |
- |
|
3521 |
- /* 업로드 완료 후에는 삭제 버튼 표시 */ |
|
3522 |
- &.complete { |
|
3523 |
- .progress { display:none; } |
|
3524 |
- .btn-delete { display:inline-block; } |
|
3525 |
- .btn-insert { display:block; } |
|
3526 |
- } |
|
3527 |
- |
|
3528 |
- /* 임시 파일 표시 */ |
|
3529 |
- &.temporary { |
|
3530 |
- i { display:inline-block; } |
|
3531 |
- } |
|
3532 |
- } |
|
3533 | 3463 |
} |
3534 | 3464 |
} |
3465 |
+ |
|
3466 |
+.attached-file { |
|
3467 |
+ display: inline-block; |
|
3468 |
+ max-width: 50%; |
|
3469 |
+ height: 30px; |
|
3470 |
+ line-height: 30px; |
|
3471 |
+ border: 1px solid #ccc; |
|
3472 |
+ background: #fafafa; |
|
3473 |
+ padding: 0 10px; |
|
3474 |
+ margin: 5px 4px; |
|
3475 |
+ cursor:pointer; |
|
3476 |
+ overflow: hidden; |
|
3477 |
+ -webkit-transition-duration: 0.5s; |
|
3478 |
+ &:hover { border:1px solid @primary; } |
|
3479 |
+ |
|
3480 |
+ i { |
|
3481 |
+ display:none; |
|
3482 |
+ margin-right:3px; |
|
3483 |
+ vertical-align: middle; |
|
3484 |
+ color:@yobi-blue; |
|
3485 |
+ } |
|
3486 |
+ .name { |
|
3487 |
+ display: inline-block; |
|
3488 |
+ max-width: 250px; |
|
3489 |
+ overflow: hidden; |
|
3490 |
+ text-overflow: ellipsis; |
|
3491 |
+ white-space:nowrap; |
|
3492 |
+ vertical-align:middle; |
|
3493 |
+ -webkit-transition-duration: 0.5s; |
|
3494 |
+ } |
|
3495 |
+ .size { font-size:11px; vertical-align:middle; } |
|
3496 |
+ |
|
3497 |
+ /* 업로드 하는 도중에는 진행 상태 표시 */ |
|
3498 |
+ .progress { |
|
3499 |
+ display: inline-block; |
|
3500 |
+ width: 100px; |
|
3501 |
+ height: 7px; |
|
3502 |
+ margin: 0; |
|
3503 |
+ overflow: hidden; |
|
3504 |
+ } |
|
3505 |
+ .btn-delete { |
|
3506 |
+ width: 30px; |
|
3507 |
+ height: 30px; |
|
3508 |
+ font-size: 1.5em; |
|
3509 |
+ font-weight: bold; |
|
3510 |
+ &:hover { color:@primary; } |
|
3511 |
+ } |
|
3512 |
+ .btn-insert { |
|
3513 |
+ display:none; |
|
3514 |
+ line-height: 20px; |
|
3515 |
+ margin-top: 2px; margin-right: 10px; |
|
3516 |
+ .box-shadow(none); |
|
3517 |
+ &:hover { background:#fff; color:@primary; } |
|
3518 |
+ } |
|
3519 |
+ |
|
3520 |
+ /* 업로드 완료 후에는 삭제 버튼 표시 */ |
|
3521 |
+ &.complete { |
|
3522 |
+ .progress { display:none; } |
|
3523 |
+ .btn-delete { display:inline-block; } |
|
3524 |
+ .btn-insert { display:block; } |
|
3525 |
+ } |
|
3526 |
+ |
|
3527 |
+ /* 임시 파일 표시 */ |
|
3528 |
+ &.temporary { |
|
3529 |
+ i { display:inline-block; } |
|
3530 |
+ } |
|
3531 |
+} |
|
3532 |
+ |
|
3535 | 3533 |
.upload-drop-here { |
3536 | 3534 |
position: absolute; |
3537 | 3535 |
top: 2px; |
... | ... | @@ -7428,3 +7426,28 @@ |
7428 | 7426 |
} |
7429 | 7427 |
} |
7430 | 7428 |
} |
7429 |
+ |
|
7430 |
+.upload-button-line { |
|
7431 |
+ .file-upload { |
|
7432 |
+ position: relative; |
|
7433 |
+ display: inline-block; |
|
7434 |
+ } |
|
7435 |
+ |
|
7436 |
+ .file-upload__label { |
|
7437 |
+ display: block; |
|
7438 |
+ border-radius: 2px; |
|
7439 |
+ transition: background .3s; |
|
7440 |
+ } |
|
7441 |
+ |
|
7442 |
+ .file-upload__input { |
|
7443 |
+ position: fixed; |
|
7444 |
+ left: 0; |
|
7445 |
+ top: 0; |
|
7446 |
+ right: 0; |
|
7447 |
+ height: 0; |
|
7448 |
+ bottom: 0; |
|
7449 |
+ width:0; |
|
7450 |
+ opacity: 0; |
|
7451 |
+ } |
|
7452 |
+ |
|
7453 |
+} |
--- app/controllers/AttachmentApp.java
+++ app/controllers/AttachmentApp.java
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 |
import play.Configuration; |
17 | 17 |
import play.Logger; |
18 | 18 |
import play.mvc.Controller; |
19 |
+import play.mvc.Http; |
|
19 | 20 |
import play.mvc.Http.MultipartFormData.FilePart; |
20 | 21 |
import play.mvc.Result; |
21 | 22 |
import utils.AccessControl; |
... | ... | @@ -168,11 +169,15 @@ |
168 | 169 |
|
169 | 170 |
public static Result deleteFile(Long id) { |
170 | 171 |
// _method must be 'delete' |
171 |
- Map<String, String[]> data = |
|
172 |
- request().body().asMultipartFormData().asFormUrlEncoded(); |
|
173 |
- if (!HttpUtil.getFirstValueFromQuery(data, "_method").toLowerCase() |
|
174 |
- .equals("delete")) { |
|
175 |
- return badRequest("_method must be 'delete'."); |
|
172 |
+ Http.MultipartFormData formData = request().body().asMultipartFormData(); |
|
173 |
+ |
|
174 |
+ if( formData != null ) { |
|
175 |
+ Map<String, String[]> data = |
|
176 |
+ formData.asFormUrlEncoded(); |
|
177 |
+ if (!HttpUtil.getFirstValueFromQuery(data, "_method").toLowerCase() |
|
178 |
+ .equals("delete")) { |
|
179 |
+ return badRequest("_method must be 'delete'."); |
|
180 |
+ } |
|
176 | 181 |
} |
177 | 182 |
|
178 | 183 |
// Remove the attachment. |
... | ... | @@ -222,6 +227,11 @@ |
222 | 227 |
return metadata; |
223 | 228 |
} |
224 | 229 |
|
230 |
+ public static Map<String, List<Map<String, String>>> getFileList(ResourceType resourceType, Long containerId) |
|
231 |
+ throws PermissionDeniedException { |
|
232 |
+ return getFileList(resourceType.toString(), String.valueOf(containerId)); |
|
233 |
+ } |
|
234 |
+ |
|
225 | 235 |
public static Map<String, List<Map<String, String>>> getFileList(String containerType, String |
226 | 236 |
containerId) throws PermissionDeniedException { |
227 | 237 |
Map<String, List<Map<String, String>>> files = |
--- app/views/board/partial_comments.scala.html
+++ app/views/board/partial_comments.scala.html
... | ... | @@ -67,7 +67,7 @@ |
67 | 67 |
<div id="comment-body-@comment.id"> |
68 | 68 |
@common.tasklistBar() |
69 | 69 |
<div class="comment-body markdown-wrap" data-via-email="@OriginalEmail.exists(comment.asResource)" data-allowed-update="@isAllowedUpdate" >@Html(Markdown.render(comment.contents, project))</div> |
70 |
- <div class="attachments pull-right" data-attachments="@toJson(AttachmentApp.getFileList(ResourceType.NONISSUE_COMMENT.toString(), comment.id.toString()))"></div> |
|
70 |
+ <div class="attachments" data-attachments="@toJson(AttachmentApp.getFileList(ResourceType.NONISSUE_COMMENT.toString(), comment.id.toString()))"></div> |
|
71 | 71 |
</div> |
72 | 72 |
</div> |
73 | 73 |
@common.childComments(post, comment, ResourceType.NONISSUE_COMMENT) |
--- app/views/board/view.scala.html
+++ app/views/board/view.scala.html
... | ... | @@ -212,6 +212,7 @@ |
212 | 212 |
<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.Sha1.js")"></script> |
213 | 213 |
<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.Tasklist.js")"></script> |
214 | 214 |
<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.SubComment.js")"></script> |
215 |
+<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.CommentAttachmentsUpdate.js")"></script> |
|
215 | 216 |
<script type="text/javascript"> |
216 | 217 |
$(document).ready(function(){ |
217 | 218 |
$yobi.loadModule("board.View", { |
+++ app/views/common/attachmentFile.scala.html
... | ... | @@ -0,0 +1,18 @@ |
1 | +@** | |
2 | +* Yona, 21st Century Project Hosting SW | |
3 | +* | |
4 | +* Copyright Yona & Yobi Authors & NAVER Corp. & NAVER LABS Corp. | |
5 | +* https://yona.io | |
6 | +**@ | |
7 | +@import models.enumeration.ResourceType | |
8 | +@import utils.AccessControl._ | |
9 | +@import humanize.Humanize._; | |
10 | + | |
11 | +@(file:Map[String, String], parentType:ResourceType, parentId:Long) | |
12 | + | |
13 | +<div class="attached-file attached-file-marker" data-name="@file.get("name")" data-href="@routes.AttachmentApp.getFile(file.get("id").toLong)" data-mime="@file.get("mimeType")"> | |
14 | + <i class="mimetype"></i> | |
15 | + <strong class="name">@file.get("name")</strong> | |
16 | + <span class="size">@binaryPrefix(file.get("size").toDouble)</span> | |
17 | + <button type="button" class="btn-transparent btn-delete" data-id="@file.get("id")">×</button> | |
18 | +</div> |
--- app/views/common/commentUpdateForm.scala.html
+++ app/views/common/commentUpdateForm.scala.html
... | ... | @@ -1,21 +1,45 @@ |
1 | 1 |
@** |
2 | 2 |
* Yona, 21st Century Project Hosting SW |
3 | 3 |
* |
4 |
-* Copyright Yona & Yobi Authors & NAVER Corp. |
|
4 |
+* Copyright Yona & Yobi Authors & NAVER Corp. & NAVER LABS Corp. |
|
5 | 5 |
* https://yona.io |
6 | 6 |
**@ |
7 |
+@import models.enumeration.ResourceType |
|
7 | 8 |
@(comment:Comment, action:String, contents:String, isAllowedUpdate:Boolean) |
8 | 9 |
@import utils.AccessControl._ |
9 | 10 |
|
10 |
-<div id="comment-editform-@comment.id" class="comment-update-form"> |
|
11 |
- <form action="@action/@comment.id" method="post"> |
|
12 |
- <input type="hidden" name="id" value="@comment.id"> |
|
11 |
+@files = @{ |
|
12 |
+ if(comment.isInstanceOf[IssueComment]) { |
|
13 |
+ AttachmentApp.getFileList(ResourceType.ISSUE_COMMENT, comment.id) |
|
14 |
+ } else { |
|
15 |
+ AttachmentApp.getFileList(ResourceType.NONISSUE_COMMENT, comment.id) |
|
16 |
+ } |
|
17 |
+} |
|
13 | 18 |
|
19 |
+@resourceType = @{ |
|
20 |
+ if(comment.isInstanceOf[IssueComment]) { |
|
21 |
+ ResourceType.ISSUE_COMMENT |
|
22 |
+ } else { |
|
23 |
+ ResourceType.NONISSUE_COMMENT |
|
24 |
+ } |
|
25 |
+} |
|
26 |
+ |
|
27 |
+<div id="comment-editform-@comment.id" class="comment-update-form"> |
|
28 |
+ <form action="@action/@comment.id" method="post" enctype="multipart/form-data"> |
|
29 |
+ <input type="hidden" name="id" value="@comment.id"> |
|
14 | 30 |
<div class="write-comment-box"> |
15 | 31 |
<div class="write-comment-wrap"> |
16 |
- @common.editor("contents", contents,"", "update-comment-body") |
|
17 |
- |
|
18 |
- <div class="right-txt comment-update-button"> |
|
32 |
+ @common.editor("contents-" + comment.id, contents,"", "update-comment-body") |
|
33 |
+ <div class="upload-drop-here"> |
|
34 |
+ <div class="msg-wrap"> |
|
35 |
+ <div class="msg">@Messages("common.attach.dropFilesHere")</div> |
|
36 |
+ </div> |
|
37 |
+ </div> |
|
38 |
+ <div class="right-txt comment-update-button upload-button-line"> |
|
39 |
+ <span class="file-upload"> |
|
40 |
+ <label for="upload-@comment.id" class="file-upload__label ybtn">@Messages("button.upload")</label> |
|
41 |
+ <input id="upload-@comment.id" class="file-upload__input" type="file" name="filePath" multiple> |
|
42 |
+ </span> |
|
19 | 43 |
@if(comment.isAuthoredBy(UserApp.currentUser())){ |
20 | 44 |
<span class="send-notification-check" data-toggle='popover' data-trigger="hover" data-placement="top" data-content="@Messages("notification.send.mail.warning")"> |
21 | 45 |
<label class="checkbox inline"> |
... | ... | @@ -30,6 +54,14 @@ |
30 | 54 |
} |
31 | 55 |
</div> |
32 | 56 |
</div> |
57 |
+ <input type="hidden" name="temporaryUploadFiles" class="temporaryUploadFiles" value=""> |
|
58 |
+ <div class="preview-@comment.id"></div> |
|
59 |
+ <div class="attachment-files"> |
|
60 |
+ @for(file <- files.get("attachments")) { |
|
61 |
+ @attachmentFile(file, resourceType, comment.id) |
|
62 |
+ } |
|
63 |
+ </div> |
|
64 |
+ <div id="upload-@comment.id" data-resourceType="@resourceType" data-resourceId="@comment.id"></div> |
|
33 | 65 |
</div> |
34 | 66 |
</form> |
35 | 67 |
</div> |
--- app/views/common/editor.scala.html
+++ app/views/common/editor.scala.html
... | ... | @@ -1,29 +1,29 @@ |
1 | 1 |
@** |
2 |
-* Yobi, Project Hosting SW |
|
2 |
+* Yona, 21st Century Project Hosting SW |
|
3 | 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. |
|
4 |
+* Copyright Yona & Yobi Authors & NAVER Corp. & NAVER LABS Corp. |
|
5 |
+* https://yona.io |
|
20 | 6 |
**@ |
21 | 7 |
@(editorName:String, textValue:String="", additionalAttr:String="", editorMode:String="content-body", viaEmail:Boolean=false) |
22 | 8 |
|
23 | 9 |
@import org.apache.commons.lang3.RandomStringUtils._ |
24 | 10 |
@import utils.TemplateHelper._ |
25 | 11 |
|
26 |
-@defining(randomAlphabetic(8)){ wrapId => |
|
12 |
+@wrapIdGen = @{ |
|
13 |
+ var split = editorName.split("-") |
|
14 |
+ if (split.length > 1) { |
|
15 |
+ split(1) |
|
16 |
+ } else { |
|
17 |
+ randomAlphabetic(8) |
|
18 |
+ } |
|
19 |
+} |
|
20 |
+ |
|
21 |
+@textareaName = @{ |
|
22 |
+ var split = editorName.split("-") |
|
23 |
+ split(0) |
|
24 |
+} |
|
25 |
+ |
|
26 |
+@defining(wrapIdGen){ wrapId => |
|
27 | 27 |
<div data-toggle="markdown-editor" class="mt10"> |
28 | 28 |
<ul class="nav nav-tabs nm small"> |
29 | 29 |
<li class="active"> |
... | ... | @@ -45,7 +45,7 @@ |
45 | 45 |
|
46 | 46 |
<div id="edit-@wrapId" class="tab-pane active"> |
47 | 47 |
<div class="textarea-box"> |
48 |
- <textarea name="@editorName" class="content comment nm" data-editor-mode="@editorMode" markdown="true" id="editor-@editorName-@wrapId" |
|
48 |
+ <textarea name="@textareaName" class="content comment nm" data-editor-mode="@editorMode" markdown="true" id="editor-@textareaName-@wrapId" |
|
49 | 49 |
@additionalAttr>@textValue</textarea> |
50 | 50 |
</div> |
51 | 51 |
</div> |
--- app/views/issue/view.scala.html
+++ app/views/issue/view.scala.html
... | ... | @@ -465,6 +465,7 @@ |
465 | 465 |
<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.Sha1.js")"></script> |
466 | 466 |
<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.Tasklist.js")"></script> |
467 | 467 |
<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.SubComment.js")"></script> |
468 |
+<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.CommentAttachmentsUpdate.js")"></script> |
|
468 | 469 |
<script type="text/javascript"> |
469 | 470 |
$(function(){ |
470 | 471 |
// yobi.issue.View |
... | ... | @@ -500,8 +501,7 @@ |
500 | 501 |
// detect comment which contains mention at me |
501 | 502 |
$(".comment-body:contains('@UserApp.currentUser().getPureNameOnly')").closest(".comment").addClass("mentioned"); |
502 | 503 |
}); |
503 |
-</script> |
|
504 |
-<script> |
|
504 |
+ |
|
505 | 505 |
$(function () { |
506 | 506 |
yonaAssgineeModule( |
507 | 507 |
"@api.routes.IssueApi.findAssignableUsers(project.owner, project.name, issue.getNumber)", |
... | ... | @@ -580,7 +580,6 @@ |
580 | 580 |
shape: 'rounded', |
581 | 581 |
tooltips: true |
582 | 582 |
}); |
583 |
- |
|
584 | 583 |
}); |
585 | 584 |
</script> |
586 | 585 |
} |
--- build.sbt
+++ build.sbt
... | ... | @@ -58,7 +58,8 @@ |
58 | 58 |
"com.google.guava" % "guava" % "19.0", |
59 | 59 |
"com.googlecode.htmlcompressor" % "htmlcompressor" % "1.4", |
60 | 60 |
"org.springframework" % "spring-jdbc" % "4.1.5.RELEASE", |
61 |
- "javax.xml.bind" % "jaxb-api" % "2.3.0" |
|
61 |
+ "javax.xml.bind" % "jaxb-api" % "2.3.0", |
|
62 |
+ "com.github.mfornos" % "humanize-slim" % "1.2.2" |
|
62 | 63 |
) |
63 | 64 |
|
64 | 65 |
libraryDependencies += "org.apache.subversion" % "svn-javahl-api" % "1.9.0" |
--- conf/messages.ko-KR
+++ conf/messages.ko-KR
... | ... | @@ -158,7 +158,7 @@ |
158 | 158 |
common.attach.drophere = 첨부할 파일을 끌어다 놓거나 |
159 | 159 |
common.attach.error.delete = 파일 삭제에 실패했습니다.<br>{1} ({0}) |
160 | 160 |
common.attach.error.upload = 파일 첨부에 실패했습니다.<br>{1} ({0}) |
161 |
-common.attach.pastehere = . 클립보드 이미지를 붙여 넣을 수도 있습니다 |
|
161 |
+common.attach.pastehere = 클립보드 이미지를 붙여 넣을 수도 있습니다 |
|
162 | 162 |
common.attachment = 첨부파일 |
163 | 163 |
common.comment = 댓글 |
164 | 164 |
common.comment.author = 댓글 작성자 |
... | ... | @@ -461,7 +461,7 @@ |
461 | 461 |
notification.reviewthread.inTheFile = {0} 에서: |
462 | 462 |
notification.reviewthread.reopened = 리뷰 스레드 다시 열림 |
463 | 463 |
notification.resource.deleted = {0} 님에 의해 삭제되었습니다 |
464 |
-notification.send.mail = 수정에 대한 알림 메일 발송 |
|
464 |
+notification.send.mail = 수정 알림 메일 발송 |
|
465 | 465 |
notification.send.mail.warning = 만약 해당글의 원 작성자가 아니라면 이 옵션은 무시되고 알림 메일이 발송됩니다. |
466 | 466 |
notification.type.comment.updated = 댓글 수정 |
467 | 467 |
notification.type.issue.assignee.changed = 이슈 담당자 변경 |
+++ public/javascripts/common/yona.CommentAttachmentsUpdate.js
... | ... | @@ -0,0 +1,131 @@ |
1 | +$(function(){ | |
2 | + function deleteAttachment() { | |
3 | + var $this = $(this); | |
4 | + var $parent = $this.parent(".attached-file-marker"); | |
5 | + var id = $this.data("id"); | |
6 | + var filename = $parent.data("name"); | |
7 | + var url = $parent.data("href"); | |
8 | + var mimeType = $parent.data("mime"); | |
9 | + var linkStr = "[" + filename + "](" + url + ")"; | |
10 | + | |
11 | + if (mimeType.startsWith("image")) { | |
12 | + linkStr = "!" + linkStr; | |
13 | + } | |
14 | + | |
15 | + var $form = $this.parent().closest("form"); | |
16 | + var $textarea = $form.find("textarea"); | |
17 | + var $attachfiles = $form.find(".temporaryUploadFiles"); | |
18 | + | |
19 | + $attachfiles.val($attachfiles.val().split(",").filter(function(item){ | |
20 | + return item != id; | |
21 | + }).join(",")); | |
22 | + $textarea.val($textarea.val().split(linkStr).join("")); | |
23 | + | |
24 | + $.post(url) | |
25 | + .done(function (data) { | |
26 | + $parent.remove(); | |
27 | + }) | |
28 | + .fail(function (data) { | |
29 | + console.log(data); | |
30 | + }); | |
31 | + } | |
32 | + | |
33 | + function insertLinkIntoTextarea($textarea, data, caretPos) { | |
34 | + var textAreaTxt = $textarea.val(); | |
35 | + var txtToAdd = "[" + data.name + "](" + data.url + ")"; | |
36 | + if (data.mimeType.startsWith("image")) { | |
37 | + txtToAdd = "!" + txtToAdd; | |
38 | + } | |
39 | + | |
40 | + txtToAdd = " " + txtToAdd; | |
41 | + | |
42 | + if (textAreaTxt.length > 0 && caretPos === 0) { | |
43 | + caretPos = textAreaTxt.length; | |
44 | + } | |
45 | + | |
46 | + $textarea.val(textAreaTxt.substring(0, caretPos) + txtToAdd + textAreaTxt.substring(caretPos)); | |
47 | + | |
48 | + return caretPos + txtToAdd.length; | |
49 | + } | |
50 | + | |
51 | + $(".attached-file-marker").on("click", ".btn-delete", deleteAttachment); | |
52 | + $(".file-upload__input").on("change", function (e) { | |
53 | + var $this = $(this); | |
54 | + var files = $this[0].files; | |
55 | + | |
56 | + NProgress.start(); | |
57 | + var doneCount = 0; | |
58 | + | |
59 | + var caretPos = $this.parent().closest("form").find("textarea")[0].selectionStart; | |
60 | + for (var i = 0; i < files.length; i++) { | |
61 | + var file = files[i]; | |
62 | + var formData = new FormData(); | |
63 | + | |
64 | + formData.append("filePath", file); | |
65 | + | |
66 | + $.ajax({ | |
67 | + url: '/files', | |
68 | + type: 'POST', | |
69 | + cache: false, | |
70 | + contentType: false, | |
71 | + processData: false, | |
72 | + data: formData | |
73 | + }).done(function (data) { | |
74 | + var $parentForm = $this.parent().closest("form"); | |
75 | + var $attachfiles = $parentForm.find(".temporaryUploadFiles"); | |
76 | + var $textarea = $parentForm.find("textarea"); | |
77 | + | |
78 | + if (doneCount === 0) { | |
79 | + $attachfiles.val(data.id); | |
80 | + } else { | |
81 | + $attachfiles.val($attachfiles.val() + "," + data.id); | |
82 | + } | |
83 | + var attachment = '<div class="attached-file attached-file-marker" data-mime="' + | |
84 | + data.mimeType.trim() + '" data-name="' + data.name + '" data-href="' + data.url + '">\n' + | |
85 | + '<strong class="name">' + data.name + '</strong>\n' + | |
86 | + '<span class="size">' + humanize.filesize(data.size) + '</span>\n' + | |
87 | + '<button type="button" class="btn-transparent btn-delete" data-id="' + data.id + '">×</button>\n' + | |
88 | + '</div>'; | |
89 | + $parentForm.find(".attachment-files").append(attachment); | |
90 | + $parentForm.find(".attachment-files").on("click", ".btn-delete", deleteAttachment); | |
91 | + | |
92 | + caretPos = insertLinkIntoTextarea($textarea, data, caretPos); | |
93 | + | |
94 | + doneCount++; | |
95 | + if (doneCount === files.length) { | |
96 | + NProgress.done(); | |
97 | + } | |
98 | + }).fail(function (data) { | |
99 | + $yobi.notify(data); | |
100 | + }); | |
101 | + } | |
102 | + }); | |
103 | + | |
104 | + var rememberBorder = ""; | |
105 | + $(".textarea-box") | |
106 | + .on("dragenter", "textarea", function(e){ | |
107 | + e.stopPropagation(); | |
108 | + e.preventDefault(); | |
109 | + rememberBorder = $(this).css("border"); | |
110 | + $(this).css("border", "1px dashed orange"); | |
111 | + }) | |
112 | + .on("dragover", "textarea", function(e){ | |
113 | + e.stopPropagation(); | |
114 | + e.preventDefault(); | |
115 | + }) | |
116 | + .on("drop", "textarea", function(e){ | |
117 | + e.stopPropagation(); | |
118 | + e.preventDefault(); | |
119 | + | |
120 | + var dt = e.originalEvent.dataTransfer; | |
121 | + var files = dt.files; | |
122 | + | |
123 | + console.log(files); | |
124 | + | |
125 | + $(this).css("border", rememberBorder); | |
126 | + $(this).parent().closest("form").find(".file-upload__input")[0].files = files; | |
127 | + }) | |
128 | + .on("dragleave", "textarea", function(e){ | |
129 | + $(this).css("border", rememberBorder); | |
130 | + }); | |
131 | +}); |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?