diff --git a/app/assets/stylesheets/less/_page.less b/app/assets/stylesheets/less/_page.less index d653d9517..0829e92f6 100644 --- a/app/assets/stylesheets/less/_page.less +++ b/app/assets/stylesheets/less/_page.less @@ -2087,7 +2087,12 @@ label.inline-list { } .filters { color:#666; - float: right; margin-top:5px; + float: right; + margin-top:5px; + border: 1px solid #ddd; + border-radius: 3px; + padding: 5px 10px; + .filter { margin-right:10px; &.active { font-weight:bold; color:@primary; } @@ -7138,8 +7143,28 @@ div.diff-body[data-outdated="true"] tr:hover .icon-comment { } .sharer-list { - margin-top: 40px; - padding: 10px; + margin-top: -5px; + padding: 15px; + + &.sharer-list-border { + border: 1px solid #ddd; + border-radius: 3px; + position: relative; + + &:before { + position:absolute; + top: -9px; + left: 133px; + content: ' '; + width: 16px; + height: 16px; + border-width: 0 0 1px 1px; + border-style: solid; + border-color: #BDC3C7; + background-color: #fff; + .rotate(135deg); + } + } .issue-share-title { font-size: 16px; diff --git a/app/controllers/ProjectApp.java b/app/controllers/ProjectApp.java index 3fcea8abf..778ff2661 100644 --- a/app/controllers/ProjectApp.java +++ b/app/controllers/ProjectApp.java @@ -1266,7 +1266,7 @@ public static Result newWebhook(String ownerId, String projectName) { Webhook webhook = addWebhookForm.get(); - Webhook.create(project.id, webhook.payloadUrl, webhook.secret, + Webhook.create(project.id, webhook.payloadUrl.trim(), webhook.secret, BooleanUtils.toBooleanDefaultIfNull(webhook.gitPushOnly, false)); return redirect(routes.ProjectApp.webhooks(project.owner, project.name)); diff --git a/app/controllers/VoteApp.java b/app/controllers/VoteApp.java index b6b1e95f3..6b190328f 100644 --- a/app/controllers/VoteApp.java +++ b/app/controllers/VoteApp.java @@ -32,6 +32,7 @@ import utils.RouteUtil; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -119,16 +120,15 @@ public static Result unvoteComment(String user, String project, Long number, Lon } public static List getVotersForAvatar(Set voters, int size){ - return getSubList(voters, 0, size); - } + List userList = new ArrayList<>(); + Iterator iterator = voters.iterator(); + int index = 0; - public static List getVotersForName(Set voters, int fromIndex, int size){ - return getSubList(voters, fromIndex, fromIndex + size); - } + while( index++ < size && iterator.hasNext() ) { + userList.add(iterator.next()); + } - public static Set getVotersExceptCurrentUser(Set voters){ - voters.remove(UserApp.currentUser()); - return voters; + return userList; } /** diff --git a/app/controllers/api/IssueApi.java b/app/controllers/api/IssueApi.java index 1e5b4665b..5d20f1d1c 100644 --- a/app/controllers/api/IssueApi.java +++ b/app/controllers/api/IssueApi.java @@ -17,10 +17,7 @@ import controllers.annotation.IsCreatable; import controllers.routes; import models.*; -import models.enumeration.Operation; -import models.enumeration.ResourceType; -import models.enumeration.State; -import models.enumeration.UserState; +import models.enumeration.*; import org.apache.commons.lang3.StringUtils; import play.db.ebean.Transactional; import play.i18n.Messages; @@ -367,6 +364,17 @@ private static ExpressionList getUserExpressionList(String query, String s return el; } + private static ExpressionList getProjectExpressionList(String query, String searchType) { + + ExpressionList el = Project.find.select("id, name").where() + .eq("projectScope", ProjectScope.PUBLIC).disjunction(); + + el.icontains("name", query); + el.endJunction(); + + return el; + } + private static void addAuthorIfNotMe(Issue issue, List users, User issueAuthor) { if (!issue.getAuthor().loginId.equals(UserApp.currentUser().loginId)) { addUserToUsersWithCustomName(issueAuthor, users, Messages.get("issue.assignToAuthor")); @@ -391,12 +399,25 @@ static void addUserToUsers(User user, List users) { userNode.put("loginId", user.loginId); userNode.put("name", user.getDisplayName()); userNode.put("avatarUrl", user.avatarUrl()); + userNode.put("type", "user"); if(!users.contains(userNode)) { users.add(userNode); } } + static void addProjectToProjects(Project project, List projects) { + ObjectNode projectNode = Json.newObject(); + projectNode.put("loginId", project.id); + projectNode.put("name", project.owner + "/" + project.name); + projectNode.put("avatarUrl", ""); + projectNode.put("type", "project"); + + if(!projects.contains(projectNode)) { + projects.add(projectNode); + } + } + private static void addUserToUsersWithCustomName(User user, List users, String name) { ObjectNode userNode = Json.newObject(); userNode.put("loginId", user.loginId); @@ -580,21 +601,27 @@ public static Result findSharableUsers(String ownerName, String projectName, Lon return status(Http.Status.NOT_ACCEPTABLE); } - List users = new ArrayList<>(); + List results = new ArrayList<>(); - ExpressionList el = getUserExpressionList(query, request().getQueryString("type")); + ExpressionList userExpressionList = getUserExpressionList(query, request().getQueryString("type")); + ExpressionList projectExpressionList = getProjectExpressionList(query, request().getQueryString("type")); - int total = el.findRowCount(); + int total = userExpressionList.findRowCount() + projectExpressionList.findRowCount(); if (total > MAX_FETCH_USERS) { - el.setMaxRows(MAX_FETCH_USERS); + userExpressionList.setMaxRows(MAX_FETCH_USERS / 2); + projectExpressionList.setMaxRows(MAX_FETCH_USERS / 2); response().setHeader("Content-Range", "items " + MAX_FETCH_USERS + "/" + total); } - for (User user :el.findList()) { - addUserToUsers(user, users); + for (User user :userExpressionList.findList()) { + addUserToUsers(user, results); } - return ok(toJson(users)); + for (Project project: projectExpressionList.findList()) { + addProjectToProjects(project, results); + } + + return ok(toJson(results)); } public static Result updateSharer(String owner, String projectName, Long number){ @@ -618,35 +645,73 @@ public static Result updateSharer(String owner, String projectName, Long number) final String action = json.findValue("action").asText(); ObjectNode result = changeSharer(sharer, issue, action); - sendNotification(sharer, issue, action); return ok(result); } private static ObjectNode changeSharer(JsonNode sharer, Issue issue, String action) { ObjectNode result = Json.newObject(); - for (JsonNode sharerLoginId : sharer) { + List users = new ArrayList<>(); + + if(sharer.findValue("type").asText().equals("project")) { + changeSharerByProject(sharer.findValue("loginId").asLong(), issue, action, result, users); + } else { + changeSharerByUser(sharer.findValue("loginId").asText(), issue, action, result, users); + } + + sendNotification(users, issue, action); + return result; + } + + private static void changeSharerByUser(String loginId, Issue issue, String action, ObjectNode result, List users) { + if ("add".equalsIgnoreCase(action)) { + addSharer(issue, loginId); + } else if ("delete".equalsIgnoreCase(action)) { + removeSharer(issue, loginId); + } else { + play.Logger.error("Unknown issue sharing action: " + issue + ":" + action + " by " + currentUser()); + } + + users.add(loginId); + setShareActionToResponse(action, result); + result.put("sharer", User.findByLoginId(loginId).getDisplayName()); + } + + private static void changeSharerByProject(Long projectId, Issue issue, String action, ObjectNode result, List users) { + List projectUsers = ProjectUser.findMemberListByProject(projectId); + + for (ProjectUser projectUser: projectUsers) { if ("add".equalsIgnoreCase(action)) { - addSharer(issue, sharerLoginId.asText()); - result.put("action", "added"); + addSharer(issue, projectUser.user.loginId); } else if ("delete".equalsIgnoreCase(action)) { - result.put("action", "deleted"); - removeSharer(issue, sharerLoginId.asText()); + removeSharer(issue, projectUser.user.loginId); } else { play.Logger.error("Unknown issue sharing action: " + issue + ":" + action + " by " + currentUser()); - result.put("action", "Do nothing. Unsupported action: " + action); } - result.put("sharer", User.findByLoginId(sharerLoginId.asText()).getDisplayName()); + users.add(projectUser.user.loginId); + } + + setShareActionToResponse(action, result); + + result.put("sharer", Project.find.byId(projectId).name); + } + + private static void setShareActionToResponse(String action, ObjectNode result) { + if ("add".equalsIgnoreCase(action)) { + result.put("action", "added"); + } else if ("delete".equalsIgnoreCase(action)) { + result.put("action", "deleted"); + } else { + result.put("action", "Do nothing. Unsupported action: " + action); } - return result; } - private static void sendNotification(JsonNode sharer, Issue issue, String action) { + private static void sendNotification(List users, Issue issue, String action) { Runnable preUpdateHook = new Runnable() { @Override public void run() { - for(JsonNode sharerLoginId: sharer){ - addSharerChangedNotification(issue, sharerLoginId.asText(), action); + for(String sharerLoginId: users){ + addSharerChangedNotification(issue, sharerLoginId, action); } } }; diff --git a/app/models/NotificationEvent.java b/app/models/NotificationEvent.java index 545dadd87..59be3fd44 100644 --- a/app/models/NotificationEvent.java +++ b/app/models/NotificationEvent.java @@ -978,11 +978,28 @@ private static Set getMandatoryReceivers(Issue issue, EventType eventType) receivers.removeAll(findUnwatchers(issue.asResource())); receivers.removeAll(findEventUnwatchersByEventType(issue.project.id, eventType)); - receivers.remove(UserApp.currentUser()); + receivers.remove(findCurrentUserToBeExcluded(issue.authorId)); return receivers; } + private static User findCurrentUserToBeExcluded(Long authorId) { + User currentUser; + try { + currentUser = UserApp.currentUser(); + } catch (RuntimeException re) { + // expectation: "There is no HTTP Context available from here" runtime exception + currentUser = User.anonymous; + } + + if (currentUser.isAnonymous()) { + // It is assumed that it is called by author and processed by system. + return User.find.byId(authorId); + } else { + return currentUser; + } + } + private static Set getMandatoryReceivers(Posting posting, EventType eventType) { Set receivers = findWatchers(posting.asResource()); receivers.add(posting.getAuthor()); @@ -991,7 +1008,7 @@ private static Set getMandatoryReceivers(Posting posting, EventType eventT receivers.removeAll(findUnwatchers(posting.asResource())); receivers.removeAll(findEventUnwatchersByEventType(posting.project.id, eventType)); - receivers.remove(UserApp.currentUser()); + receivers.remove(findCurrentUserToBeExcluded(posting.authorId)); return receivers; } @@ -1006,16 +1023,16 @@ private static Set getMandatoryReceivers(Comment comment, EventType eventT receivers.removeAll(findUnwatchers(parent.asResource())); receivers.removeAll(findEventUnwatchersByEventType(comment.projectId, eventType)); - receivers.remove(UserApp.currentUser()); + receivers.remove(findCurrentUserToBeExcluded(comment.authorId)); return receivers; } - private static Set getProjectCommitReceivers(Project project, EventType eventType) { + private static Set getProjectCommitReceivers(Project project, EventType eventType, User sender) { Set receivers = findMembersOnlyFromWatchers(project); receivers.removeAll(findUnwatchers(project.asResource())); receivers.removeAll(findEventUnwatchersByEventType(project.id, eventType)); - receivers.remove(UserApp.currentUser()); + receivers.remove(sender); return receivers; } @@ -1042,7 +1059,7 @@ private static Set extractMembers(Project project) { private static Set getReceiversForIssueBodyChanged(String oldBody, Issue issue) { Set receivers = getMandatoryReceivers(issue, ISSUE_BODY_CHANGED); receivers.addAll(getNewMentionedUsers(oldBody, issue.body)); - receivers.remove(UserApp.currentUser()); + receivers.remove(findCurrentUserToBeExcluded(issue.authorId)); return receivers; } @@ -1183,7 +1200,7 @@ public static void afterOrganizationMemberRequest(Organization organization, Use public static void afterNewCommits(List commits, List refNames, Project project, User sender, String title) { NotificationEvent notiEvent = createFrom(sender, project); notiEvent.title = title; - notiEvent.receivers = getProjectCommitReceivers(project, NEW_COMMIT); + notiEvent.receivers = getProjectCommitReceivers(project, NEW_COMMIT, sender); notiEvent.eventType = NEW_COMMIT; notiEvent.oldValue = null; notiEvent.newValue = newCommitsMessage(commits, refNames, project); diff --git a/app/models/Project.java b/app/models/Project.java index f443e04b7..aeeec732c 100644 --- a/app/models/Project.java +++ b/app/models/Project.java @@ -191,7 +191,7 @@ public Set findAuthorsAndWatchers() { } private Set getIssueUsers() { - String issueSql = "SELECT distinct author_id id FROM ISSUE where project_id=" + this.id; + String issueSql = "select distinct author_id id from issue where project_id=" + this.id; return User.find.setRawSql(RawSqlBuilder.parse(issueSql).create()).findSet(); } diff --git a/app/models/ProjectUser.java b/app/models/ProjectUser.java index 64034708e..e085dadd0 100644 --- a/app/models/ProjectUser.java +++ b/app/models/ProjectUser.java @@ -61,7 +61,10 @@ public static void create(Long userId, Long projectId, Long roleId) { } public static void delete(Long userId, Long projectId) { - ProjectUser.findByIds(userId, projectId).delete(); + ProjectUser projectUser = ProjectUser.findByIds(userId, projectId); + if (projectUser != null) { + projectUser.delete(); + } } public static void assignRole(Long userId, Long projectId, Long roleId) { @@ -87,8 +90,12 @@ public static void assignRole(Long userId, Long projectId, RoleType roleType) { } public static ProjectUser findByIds(Long userId, Long projectId) { - return find.where().eq("user.id", userId).eq("project.id", projectId) - .ne("role.id", RoleType.SITEMANAGER.roleType()).findUnique(); + List projectUsers = find.where().eq("user.id", userId).eq("project.id", projectId) + .ne("role.id", RoleType.SITEMANAGER.roleType()).findList(); + if(projectUsers.size() > 0) { + return projectUsers.get(0); + } + return null; } public static List findMemberListByProject(Long projectId) { diff --git a/app/models/User.java b/app/models/User.java index 5dea11411..d41136f9b 100644 --- a/app/models/User.java +++ b/app/models/User.java @@ -1052,6 +1052,10 @@ public String extractDepartmentPart(){ } public String getDisplayName(){ + if (UserApp.currentUser().isAnonymous()) { + return name; + } + if (StringUtils.isNotBlank(englishName) && lang != null && UserApp.currentUser().lang.startsWith("en")) { return englishName + " " + extractDepartmentPart(); } else { diff --git a/app/utils/AccessControl.java b/app/utils/AccessControl.java index d6da68297..fe69ae641 100644 --- a/app/utils/AccessControl.java +++ b/app/utils/AccessControl.java @@ -1,23 +1,9 @@ /** - * Yobi, Project Hosting SW - * - * Copyright 2012 NAVER Corp. - * http://yobi.io - * - * @author Yi EungJun - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ + * Yona, 21st Century Project Hosting SW + *

+ * Copyright Yona & Yobi Authors & NAVER Corp. & NAVER LABS Corp. + * https://yona.io + **/ package utils; import models.*; @@ -269,7 +255,8 @@ private static boolean isProjectResourceAllowed(User user, Project project, Reso case ATTACHMENT: switch (operation) { case READ: - return isAllowed(user, resource.getContainer(), Operation.READ); + return isAllowed(user, resource.getContainer(), Operation.READ) + || isAllowedIfSharer(user, resource.getContainer()); case UPDATE: case DELETE: return isAllowed(user, resource.getContainer(), Operation.UPDATE); @@ -381,9 +368,16 @@ private static boolean isAllowedIfAuthor(User user, Resource resource) { private static boolean isAllowedIfSharer(User user, Resource resource) { switch (resource.getType()) { case ISSUE_POST: - case ISSUE_COMMENT: Issue issue = Issue.finder.byId(Long.valueOf(resource.getId())); + if (issue != null && issue.parent != null) { + if (Optional.ofNullable(issue.parent.findSharerByUserId(user.id)).isPresent()) { + return true; + } + } return issue != null && Optional.ofNullable(issue.findSharerByUserId(user.id)).isPresent(); + case ISSUE_COMMENT: + IssueComment issueComment = IssueComment.find.byId(Long.valueOf(resource.getId())); + return issueComment != null && isAllowedIfSharer(user, issueComment.issue.asResource()); default: return false; } diff --git a/app/views/issue/partial_comment.scala.html b/app/views/issue/partial_comment.scala.html index 30b4e1bfb..8f35e2a49 100644 --- a/app/views/issue/partial_comment.scala.html +++ b/app/views/issue/partial_comment.scala.html @@ -44,7 +44,7 @@ @defining(comment.asInstanceOf[IssueComment]) { issueComment => @if(issueComment.voters.size > VOTER_AVATAR_SHOW_LIMIT) { diff --git a/app/views/issue/partial_view_childIssueList.scala.html b/app/views/issue/partial_view_childIssueList.scala.html new file mode 100644 index 000000000..558fdd46f --- /dev/null +++ b/app/views/issue/partial_view_childIssueList.scala.html @@ -0,0 +1,56 @@ +@** +* Yona, 21st Century Project Hosting SW +* +* Copyright Yona & Yobi Authors & NAVER Corp. & NAVER LABS Corp. +* https://yona.io +**@ + +@(issue:Issue, project:Project) + +@import utils.TemplateHelper._ + +@parentIssueId = @{ + if(issue.parent == null) { + issue.id + } else { + issue.parent.id + } +} + +@defining(Issue.findByParentIssueIdAndState(parentIssueId, State.OPEN)) { openChildIssues => + @defining(Issue.findByParentIssueIdAndState(parentIssueId, State.CLOSED)) { closedChildIssues => + @if(!openChildIssues.isEmpty || !closedChildIssues.isEmpty) { +

+ @defining(Issue.finder.byId(parentIssueId)) { parentIssue => +
#@parentIssue.getNumber @parentIssue.title @if(parentIssue.assignee != null) {- @parentIssue.assignee.user.getPureNameOnly} + @defining(getPercent(closedChildIssues.size.toDouble, (openChildIssues.size + closedChildIssues.size).toDouble)) { percentage => +
+
+
+ @if(percentage != 100){@closedChildIssues.size/}@(openChildIssues.size + closedChildIssues.size) + @Messages("issue.state." + parentIssue.state.state) +
+ } + } +
+ @for(childIssue <- openChildIssues) { + + } + @for(childIssue <- closedChildIssues) { + + } +
+ } + } +} + diff --git a/app/views/issue/partial_voters.scala.html b/app/views/issue/partial_voters.scala.html index a70f1d921..60bff49eb 100644 --- a/app/views/issue/partial_voters.scala.html +++ b/app/views/issue/partial_voters.scala.html @@ -14,14 +14,14 @@
  • @Html(getUserAvatar(UserApp.currentUser, "smaller"))
  • } - @defining(VoteApp.getVotersExceptCurrentUser(issue.voters)) { issueVoters => + @defining(issue.voters) { issueVoters => @for(voter <- VoteApp.getVotersForAvatar(issueVoters, numOfAvatars)) {
  • @Html(getUserAvatar(voter, "smaller"))
  • } @if(issueVoters.size > numOfAvatars) {
  • - @defining(Issue.finder.byId(parentIssueId)) { parentIssue => -
    #@parentIssue.getNumber @parentIssue.title @if(parentIssue.assignee != null) {- @parentIssue.assignee.user.getPureNameOnly} - @defining(getPercent(closedChildIssues.size.toDouble, (openChildIssues.size + closedChildIssues.size).toDouble)) { percentage => -
    -
    -
    - @if(percentage != 100){@closedChildIssues.size/}@(openChildIssues.size + closedChildIssues.size) - @Messages("issue.state." + parentIssue.state.state) -
    - } - } -
    - @for(childIssue <- openChildIssues) { - - } - @for(childIssue <- closedChildIssues) { - - } - +@conatinsCurrentUserInWatchers = @{Watch.isWatching(UserApp.currentUser(), issue.asResource())} + +@isThisParentIssue() = @{ + issue.parent == null +} + +@amIShared() = @{ + var found = false + for(sharer <- issue.sharers) { + if(sharer.user.id.equals(UserApp.currentUser().id)) { + found = true } } + found } -} - -@conatinsCurrentUserInWatchers = @{Watch.isWatching(UserApp.currentUser(), issue.asResource())} @projectLayout(titleForOGTag, project, utils.MenuType.ISSUE){ @projectMenu(project, utils.MenuType.ISSUE, "main-menu-only") @@ -193,7 +168,7 @@ } @if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.UPDATE) && !hasSharer) { - + } @@ -259,10 +234,12 @@
    - @if(issue.parent == null) { - @showChildIssues(issue.id) + @if(amIShared) { + @if(isThisParentIssue) { + @partial_view_childIssueList(issue, project) + } } else { - @showChildIssues(issue.parent.id) + @partial_view_childIssueList(issue, project) }
    @** Comment **@ @@ -517,7 +494,7 @@

    @Messages("issue.delete")

    $('#issue-share-button').on('click', function () { $('#sharer-list').show(); - $('.sharer-list').show(); + $('.sharer-list').show().addClass("sharer-list-border"); }); $('#translate').one('click', function (e) { diff --git a/build.sbt b/build.sbt index 1b2490cc5..1046667aa 100644 --- a/build.sbt +++ b/build.sbt @@ -3,7 +3,7 @@ import java.nio.file.Paths name := """yona""" -version := "1.9.0" +version := "1.9.1" libraryDependencies ++= Seq( // Add your project dependencies here, diff --git a/conf/evolutions/default/20.sql b/conf/evolutions/default/20.sql new file mode 100644 index 000000000..b84608701 --- /dev/null +++ b/conf/evolutions/default/20.sql @@ -0,0 +1,25 @@ +# --- !Ups +ALTER TABLE attachment DROP CONSTRAINT IF EXISTS ck_attachment_container_type; +ALTER TABLE comment_thread DROP CONSTRAINT IF EXISTS ck_comment_thread_state; +ALTER TABLE comment_thread DROP CONSTRAINT IF EXISTS ck_comment_thread_start_side; +ALTER TABLE comment_thread DROP CONSTRAINT IF EXISTS ck_comment_thread_end_side; +ALTER TABLE commit_comment DROP CONSTRAINT IF EXISTS ck_commit_comment_side; +ALTER TABLE issue DROP CONSTRAINT IF EXISTS ck_issue_state; +ALTER TABLE issue_event DROP CONSTRAINT IF EXISTS ck_issue_event_event_type; +ALTER TABLE mention DROP CONSTRAINT IF EXISTS ck_mention_resource_type; +ALTER TABLE milestone DROP CONSTRAINT IF EXISTS ck_milestone_state; +ALTER TABLE notification_event DROP CONSTRAINT IF EXISTS ck_notification_event_resource_type; +ALTER TABLE notification_event DROP CONSTRAINT IF EXISTS ck_notification_event_event_type; +ALTER TABLE original_email DROP CONSTRAINT IF EXISTS ck_original_email_resource_type; +ALTER TABLE project DROP CONSTRAINT IF EXISTS ck_project_project_scope; +ALTER TABLE property DROP CONSTRAINT IF EXISTS ck_property_name; +ALTER TABLE pull_request DROP CONSTRAINT IF EXISTS ck_pull_request_state; +ALTER TABLE pull_request_commit DROP CONSTRAINT IF EXISTS ck_pull_request_commit_state; +ALTER TABLE pull_request_event DROP CONSTRAINT IF EXISTS ck_pull_request_event_event_type; +ALTER TABLE unwatch DROP CONSTRAINT IF EXISTS ck_unwatch_resource_type; +ALTER TABLE n4user DROP CONSTRAINT IF EXISTS ck_n4user_state; +ALTER TABLE user_project_notification DROP CONSTRAINT IF EXISTS ck_user_project_notification_notification_type; +ALTER TABLE watch DROP CONSTRAINT IF EXISTS ck_watch_resource_type; + +# --- !Downs + diff --git a/conf/messages b/conf/messages index e12fbeabb..ac0ebcba8 100644 --- a/conf/messages +++ b/conf/messages @@ -315,6 +315,7 @@ issue.noMilestone = No milestone issue.option = Option issue.search = Search Issues issue.sharer = Issue Sharer +issue.sharer.description = You can share this issue with a user or whole members of a project. If this project is private, then being shared user can only access this issue and its subtasks. issue.sharer.select = Select Issue Sharer issue.state = Status issue.state.all = All diff --git a/conf/messages.ko-KR b/conf/messages.ko-KR index 01ac2224b..3da9c1a7a 100644 --- a/conf/messages.ko-KR +++ b/conf/messages.ko-KR @@ -315,6 +315,7 @@ issue.noMilestone = 마일스톤 없음 issue.option = 이슈 옵션 issue.search = 이슈 검색 issue.sharer = 이슈 공유 +issue.sharer.description = 이 이슈를 다른 사용자와 공유합니다. 만약 공유대상을 프로젝트로 지정할 경우 해당 프로젝트 멤버 전체에게 현재 이슈를 공유합니다 . 비공개 프로젝트의 이슈일 경우, 공유된 사용자는 오직 현재 이슈와 현재 이슈의 서브태스크에만 접근 가능합니다. issue.sharer.select = 이슈 공유 대상 선택 issue.state = 상태 issue.state.all = 전체 diff --git a/public/javascripts/lib/atjs/jquery.atwho.js b/public/javascripts/lib/atjs/jquery.atwho.js index 5e4515ea4..366cd3ce0 100644 --- a/public/javascripts/lib/atjs/jquery.atwho.js +++ b/public/javascripts/lib/atjs/jquery.atwho.js @@ -272,6 +272,9 @@ }; App.prototype.dispatch = function(e) { + if (e === undefined) { + return; + } var _, c, ref, results; ref = this.controllers; results = []; diff --git a/public/javascripts/service/yona.issue.Sharer.js b/public/javascripts/service/yona.issue.Sharer.js index 69ad9ba31..4f6738ab9 100644 --- a/public/javascripts/service/yona.issue.Sharer.js +++ b/public/javascripts/service/yona.issue.Sharer.js @@ -78,7 +78,7 @@ function yonaIssueSharerModule(findUsersByloginIdsApiUrl, findSharableUsersApiUr }); $issueSharer.on("select2-selecting", function(selected) { - var data = { sharer: [selected.val], action: 'add' }; + var data = { sharer: {loginId: selected.object.loginId, type: selected.object.type}, action: 'add'}; if(updateSharingApiUrl){ $.ajax(updateSharingApiUrl, { @@ -93,7 +93,7 @@ function yonaIssueSharerModule(findUsersByloginIdsApiUrl, findSharableUsersApiUr }); $issueSharer.on("select2-removing", function(selected) { - var data = { sharer: [selected.val], action: 'delete' }; + var data = { sharer: {loginId: selected.choice.loginId, type: selected.choice.type}, action: 'delete'}; if(updateSharingApiUrl){ $.ajax(updateSharingApiUrl, {