THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

test
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

300 lines
15KB

  1. @**
  2. * Yona, 21st Century Project Hosting SW
  3. *
  4. * Copyright Yona & Yobi Authors & NAVER Corp.
  5. * https://yona.io
  6. **@
  7. @(post:Posting, commentForm: play.data.Form[PostingComment], project:Project)
  8. @import org.apache.commons.lang.StringUtils
  9. @import utils.JodaDateUtil
  10. @import utils.TemplateHelper._
  11. @import utils.AccessControl._
  12. @import models.enumeration._
  13. @import play.libs.Json.toJson
  14. @import utils.Markdown
  15. @import controllers.api.IssueApi
  16. @implicitField = @{ helper.FieldConstructor(simpleForm) }
  17. @urlToPostings = @{
  18. requestHeader.headers.get("Referer") match {
  19. case Some(url) => { urlToList(url, routes.BoardApp.posts(project.owner, project.name).toString) }
  20. case _ => routes.BoardApp.posts(project.owner, project.name).toString
  21. }
  22. }
  23. @titleForOGTag = @{post.title + " |:| " + post.body.substring(0, Math.min(post.body.length, 200))}
  24. @conatinsCurrentUserInWatchers = @{Watch.isWatching(UserApp.currentUser(), post.asResource())}
  25. @isAllowedUpdate = @{
  26. isAllowed(UserApp.currentUser(), post.asResource(), Operation.UPDATE)
  27. }
  28. @projectLayout(titleForOGTag, project, utils.MenuType.BOARD){
  29. @projectMenu(project, utils.MenuType.BOARD, "main-menu-only")
  30. <div class="page-wrap-outer">
  31. <div class="project-page-wrap board-view">
  32. @** Post Info **@
  33. <div class="board-header issue">
  34. <div class="pull-right mr10 mt10 hide-in-mobile">
  35. <div class="date" title="@JodaDateUtil.getDateString(post.createdDate)">
  36. @agoOrDateString(post.createdDate)
  37. </div>
  38. </div>
  39. <div class="title">
  40. <strong class="board-id">#@post.getNumber</strong> @post.title
  41. <div class="pull-right hide show-in-mobile" style="font-size: 0.7em">
  42. <span class="date" title="@JodaDateUtil.getDateString(post.createdDate)">
  43. @agoOrDateString(post.createdDate)
  44. </span>
  45. </div>
  46. </div>
  47. </div>
  48. @** Content body **@
  49. <div class="board-body row-fluid">
  50. <div class="span9 span-left-pane">
  51. <div class="author-info">
  52. <a href="@userInfo(post.authorLoginId)" class="usf-group">
  53. <span class="avatar-wrap smaller">
  54. <img src="@User.findByLoginId(post.authorLoginId).avatarUrl(32)" width="20" height="20">
  55. </span>
  56. @if(post.authorLoginId != null){
  57. <strong class="name">@post.getAuthor.name</strong>
  58. <span class="loginid"> <strong>@{"@"}</strong>@post.authorLoginId</span>
  59. } else {
  60. <strong class="name">@Messages("common.noAuthor")</strong>
  61. }
  62. </a>
  63. @if(StringUtils.isNotEmpty(post.history)){
  64. @if(UserApp.currentUser().isAnonymous){
  65. <div class="posting-history">
  66. <a href="@routes.UserApp.loginForm()?redirectUrl=@routes.BoardApp.post(project.owner, project.name, post.getNumber)" data-toggle="modal">@Messages("change.history")</a>
  67. </div>
  68. } else {
  69. <div class="posting-history">
  70. <a href="#-yona-posting-history" data-toggle="modal">@Messages("change.history")</a>
  71. @common.partial_history(post)
  72. </div>
  73. }
  74. }
  75. </div>
  76. @if(StringUtils.isEmpty(post.body)){
  77. <div class="content empty-content"></div>
  78. } else {
  79. <div id="post-@post.getNumber" class="hide">
  80. <form action="@api.routes.BoardApi.updatePostingContent(project.owner, project.name, post.getNumber)">
  81. <textarea>@post.body</textarea>
  82. </form>
  83. </div>
  84. <div id="post-body-@post.getNumber">
  85. @common.tasklistBar()
  86. <div class="content markdown-wrap" data-allowed-update="@isAllowedUpdate">@Html(Markdown.render(post.body, post.project))</div>
  87. </div>
  88. }
  89. <div class="attachments" id="attachments" data-attachments="@toJson(AttachmentApp.getFileList(ResourceType.BOARD_POST.toString(), post.id.toString()))"></div>
  90. <div class="board-actrow right-txt">
  91. <div class="pull-left">
  92. <div>
  93. @if(isAllowed(UserApp.currentUser(), post.asResource(), Operation.WATCH)) {
  94. <button id="watch-button" type="button" class="ybtn @if(conatinsCurrentUserInWatchers) {ybtn-watching}"
  95. data-toggle="tooltip" data-placement="top" title="@Messages("issue.watch.description")" data-watching="@conatinsCurrentUserInWatchers">
  96. @if(conatinsCurrentUserInWatchers) {
  97. @Messages("post.unwatch")
  98. } else {
  99. @Messages("post.watch")
  100. }
  101. </button>
  102. }
  103. </div>
  104. </div>
  105. @if(StringUtils.isNotBlank(IssueApi.TRANSLATION_API)) {
  106. <button type="button" id="translate" class="icon btn-transparent-with-fontsize-lineheight ml10" data-toggle="tooltip" title="@Messages("button.translation")"><i class="yobicon-lang"></i></button>
  107. }
  108. <span class="">
  109. @if(isAllowed(UserApp.currentUser(), post.asResource(), Operation.UPDATE)) {
  110. <button type="button" class="icon btn-transparent-with-fontsize-lineheight ml10 pt5px" data-toggle="tooltip" title="@Messages("button.edit")" onclick="window.location = '@routes.BoardApp.editPostForm(project.owner, project.name, post.getNumber)'"><i class="yobicon-edit-2"></i></button>
  111. } else {
  112. <a href="@routes.BoardApp.editPostForm(project.owner, project.name, post.getNumber)"><button type="button" class="icon btn-transparent-with-fontsize-lineheight ml10 pt5px" data-toggle="tooltip" title="@Messages("button.show.original")"><i class="yobicon-edit-2"></i></button></a>
  113. }
  114. @if(isAllowed(UserApp.currentUser(), post.asResource(), Operation.DELETE)) {
  115. <a href="#deleteConfirm" data-toggle="modal">
  116. <button type="button" class="icon btn-transparent-with-fontsize-lineheight ml6" data-toggle='tooltip' title="@Messages("button.delete")"><i class="yobicon-trash"></i></button></a>
  117. }
  118. </span>
  119. </div>
  120. <div class="watcher-list"></div>
  121. @** Comment **@
  122. <div id="comments" class="board-comment-wrap">
  123. <div id="timeline">
  124. <div class="timeline-list">
  125. @partial_comments(project, post)
  126. </div>
  127. </div>
  128. @common.commentForm(post.asResource(), ResourceType.NONISSUE_COMMENT, routes.BoardApp.newComment(project.owner, project.name, post.getNumber).toString())
  129. </div>
  130. @** // Comment **@
  131. </div>
  132. <div class="span3 span-right-pane mb20">
  133. <div class="issue-info board-labels">
  134. <dl>
  135. @if(project.menuSetting.board) {
  136. <dd class="project-btn-item">
  137. <a href="@routes.BoardApp.newPostForm(project.owner, project.name)" class="ybtn ybtn-success">@Messages("post.write")</a>
  138. </dd>
  139. }
  140. </dl>
  141. @**<!-- labels -->**@
  142. @if(!IssueLabel.findByProject(project).isEmpty){
  143. @if(isAllowedUpdate){
  144. @issue.partial_select_label(IssueLabel.findByProject(project), post.getLabelIds, "", "", project)
  145. } else {
  146. @issue.partial_show_selected_label(post.labels.toList, "")
  147. }
  148. }
  149. @**<!-- // -->**@
  150. <div class="right-menu-icons">
  151. @if(isAllowed(UserApp.currentUser(), post.asResource(), Operation.UPDATE)) {
  152. <button type="button" class="icon btn-transparent-with-fontsize-lineheight ml10 pt5px" data-toggle="tooltip" title="@Messages("button.edit")" onclick="window.location='@routes.BoardApp.editPostForm(project.owner, project.name, post.getNumber)'"><i class="yobicon-edit-2"></i></button>
  153. } else {
  154. <a href="@routes.BoardApp.editPostForm(project.owner, project.name, post.getNumber)"><button type="button" class="icon btn-transparent-with-fontsize-lineheight ml10 pt5px" data-toggle="tooltip" title="@Messages("button.show.original")"><i class="yobicon-edit-2"></i></button></a>
  155. }
  156. @if(isAllowed(UserApp.currentUser(), post.asResource(), Operation.DELETE)) {
  157. <a href="#deleteConfirm" data-toggle="modal">
  158. <button type="button" class="icon btn-transparent-with-fontsize-lineheight ml6" data-toggle='tooltip' title="@Messages("button.delete")"><i class="yobicon-trash"></i></button></a>
  159. }
  160. </div>
  161. </div>
  162. </div>
  163. </div>
  164. <div class="board-footer">
  165. @help.keymap("boardDetail", project)
  166. </div>
  167. </div>
  168. <script type="text/x-jquery-tmpl" id="tplAttachedFile"><!--
  169. --><li class="attached-file" data-name="${fileName}" data-href="${fileHref}" data-mime="${mimeType}" data-size="${fileSize}">
  170. <strong>${fileName}(${fileSizeReadable})${notice}</strong><!--
  171. --><a class="attached-delete"><i class="ico btn-delete"></i></a></li>
  172. </script>
  173. @** Confirm to delete post **@
  174. <div id="deleteConfirm" class="modal hide fade">
  175. <div class="modal-header">
  176. <button type="button" class="close" data-dismiss="modal">×</button>
  177. <h3>@Messages("issue.delete")</h3>
  178. </div>
  179. <div class="modal-body">
  180. <p>@Messages("post.delete.confirm")</p>
  181. </div>
  182. <div class="modal-footer">
  183. <button type="button" class="ybtn ybtn-danger" data-request-method="delete" data-request-uri="@routes.BoardApp.deletePost(project.owner, project.name, post.getNumber)">@Messages("button.yes")</button>
  184. <button type="button" class="ybtn" data-dismiss="modal">@Messages("button.no")</button>
  185. </div>
  186. </div>
  187. </div>
  188. @common.markdown(project)
  189. @common.commentDeleteModal()
  190. <link rel="stylesheet" type="text/css" media="screen" href="@routes.IssueLabelApp.labelStyles(project.owner, project.name)">
  191. <link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("javascripts/lib/atjs/jquery.atwho.css")">
  192. <link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("javascripts/lib/elevator/jquery.elevator.css")">
  193. <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/atjs/jquery.caret.min.js")"></script>
  194. <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/atjs/jquery.atwho.js")"></script>
  195. <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/elevator/jquery.elevator.js")"></script>
  196. <script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.Sha1.js")"></script>
  197. <script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.Tasklist.js")"></script>
  198. <script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.SubComment.js")"></script>
  199. <script type="text/javascript">
  200. $(document).ready(function(){
  201. $yobi.loadModule("board.View", {
  202. "sWatchUrl" : "@routes.WatchApp.watch(post.asResource.asParameter)",
  203. "sUnwatchUrl": "@routes.WatchApp.unwatch(post.asResource.asParameter)"
  204. });
  205. // yobi.ShortcutKey
  206. yobi.ShortcutKey.setKeymapLink({
  207. "L": "@urlToPostings"
  208. @if(project.menuSetting.board) {
  209. ,"N": "@routes.BoardApp.newPostForm(project.owner, project.name)"
  210. }
  211. @if(isAllowedUpdate){
  212. ,"E": "@routes.BoardApp.editPostForm(project.owner, project.name, post.getNumber)"
  213. }
  214. });
  215. // yobi.Mention
  216. yobi.Mention({
  217. "target": "textarea[id^=editor-]",
  218. "url" : "@Html(routes.ProjectApp.mentionList(project.owner, project.name, post.getNumber, post.asResource().getType.resource()).toString())"
  219. });
  220. $('.board-labels select').on('change', function(e){
  221. var url = "@Html(controllers.api.routes.BoardApi.updatePostLabel(project.owner, project.name, post.getNumber).toString())";
  222. $.ajax({
  223. url: url,
  224. method: "POST",
  225. data: JSON.stringify(e.val),
  226. contentType: "application/json; charset=UTF-8"
  227. }).done(function(response) {
  228. }).error(function(response){
  229. });
  230. });
  231. // detect comment which contains mention at me
  232. $(".comment-body:contains('@UserApp.currentUser().getPureNameOnly')").closest(".comment").addClass("mentioned");
  233. $('#translate').one('click', function (e) {
  234. var data = {
  235. owner: "@project.owner",
  236. projectName: "@project.name",
  237. type: "posting",
  238. number: "@post.getNumber"
  239. };
  240. $.ajax({
  241. url: "/-_-api/v1/translation",
  242. data: JSON.stringify(data),
  243. type: "POST",
  244. dataType: "json",
  245. contentType: "application/json"
  246. }).done(function (data) {
  247. $(".markdown-wrap").first().html(data.translated);
  248. $(this).attr("disabled", true);
  249. });
  250. });
  251. $('.comment-translate').one('click', function (e) {
  252. var payload = {
  253. owner: "@project.owner",
  254. projectName: "@project.name",
  255. type: "post-comment",
  256. number: $(this).data("commentId")
  257. };
  258. $.ajax({
  259. url: "/-_-api/v1/translation",
  260. data: JSON.stringify(payload),
  261. type: "POST",
  262. dataType: "json",
  263. contentType: "application/json"
  264. }).done(function (data) {
  265. $("#comment-body-" + payload.number).find(".markdown-wrap").html(data.translated)
  266. $(this).attr("disabled", true);
  267. });
  268. });
  269. $.elevator({
  270. shape: 'rounded',
  271. glass: true
  272. });
  273. });
  274. </script>
  275. @common.select2()
  276. }