From 30d984228e45753c2d54d43739d2f2f00c9c4211 Mon Sep 17 00:00:00 2001 From: Wonjun Hong Date: Mon, 5 Mar 2018 22:37:33 +0900 Subject: [PATCH 01/16] webhook: Remove whitespace from url on adding (#359) --- app/controllers/ProjectApp.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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)); From 262ed97f2c511d465c5e03237b037283ef6f6f1f Mon Sep 17 00:00:00 2001 From: Suwon Chae Date: Wed, 7 Mar 2018 15:32:07 +0900 Subject: [PATCH 02/16] db: Remove table's check constraints Table check constraints is available from MariaDB 10.2. It causes some problems to previous users and new users. So we decided to remove all tables check constraints. --- conf/evolutions/default/20.sql | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 conf/evolutions/default/20.sql 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 + From a4bf0b4981864872ae295d6c5ba9101aa539ff1b Mon Sep 17 00:00:00 2001 From: Mijeong Park Date: Sat, 10 Mar 2018 12:24:55 +0900 Subject: [PATCH 03/16] issue: Modify issue sharer scope for including public project --- app/controllers/api/IssueApi.java | 112 ++++++++++++++---- .../javascripts/service/yona.issue.Sharer.js | 6 +- 2 files changed, 93 insertions(+), 25 deletions(-) diff --git a/app/controllers/api/IssueApi.java b/app/controllers/api/IssueApi.java index 1e5b4665b..f38cd12f6 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.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,74 @@ 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) { + System.out.println("[action] " + 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/public/javascripts/service/yona.issue.Sharer.js b/public/javascripts/service/yona.issue.Sharer.js index 69ad9ba31..bc6e1c2fc 100644 --- a/public/javascripts/service/yona.issue.Sharer.js +++ b/public/javascripts/service/yona.issue.Sharer.js @@ -78,7 +78,8 @@ function yonaIssueSharerModule(findUsersByloginIdsApiUrl, findSharableUsersApiUr }); $issueSharer.on("select2-selecting", function(selected) { - var data = { sharer: [selected.val], action: 'add' }; + var sharer = selected.object; + var data = { sharer: {loginId: sharer.loginId, type: sharer.type}, action: 'add'}; if(updateSharingApiUrl){ $.ajax(updateSharingApiUrl, { @@ -93,7 +94,8 @@ function yonaIssueSharerModule(findUsersByloginIdsApiUrl, findSharableUsersApiUr }); $issueSharer.on("select2-removing", function(selected) { - var data = { sharer: [selected.val], action: 'delete' }; + var sharer = selected.choice; + var data = { sharer: {loginId: sharer.loginId, type: sharer.type}, action: 'delete'}; if(updateSharingApiUrl){ $.ajax(updateSharingApiUrl, { From cbbaefd84eb5fa9a3d765d6488e2ad91777aa16c Mon Sep 17 00:00:00 2001 From: Mijeong Park Date: Mon, 12 Mar 2018 15:12:12 +0900 Subject: [PATCH 04/16] issue: Refactor request data and project name for readability --- app/controllers/api/IssueApi.java | 3 +-- public/javascripts/service/yona.issue.Sharer.js | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/IssueApi.java b/app/controllers/api/IssueApi.java index f38cd12f6..5d20f1d1c 100644 --- a/app/controllers/api/IssueApi.java +++ b/app/controllers/api/IssueApi.java @@ -409,7 +409,7 @@ static void addUserToUsers(User user, List users) { static void addProjectToProjects(Project project, List projects) { ObjectNode projectNode = Json.newObject(); projectNode.put("loginId", project.id); - projectNode.put("name", project.name); + projectNode.put("name", project.owner + "/" + project.name); projectNode.put("avatarUrl", ""); projectNode.put("type", "project"); @@ -707,7 +707,6 @@ private static void setShareActionToResponse(String action, ObjectNode result) { } private static void sendNotification(List users, Issue issue, String action) { - System.out.println("[action] " + action); Runnable preUpdateHook = new Runnable() { @Override public void run() { diff --git a/public/javascripts/service/yona.issue.Sharer.js b/public/javascripts/service/yona.issue.Sharer.js index bc6e1c2fc..4f6738ab9 100644 --- a/public/javascripts/service/yona.issue.Sharer.js +++ b/public/javascripts/service/yona.issue.Sharer.js @@ -78,8 +78,7 @@ function yonaIssueSharerModule(findUsersByloginIdsApiUrl, findSharableUsersApiUr }); $issueSharer.on("select2-selecting", function(selected) { - var sharer = selected.object; - var data = { sharer: {loginId: sharer.loginId, type: sharer.type}, action: 'add'}; + var data = { sharer: {loginId: selected.object.loginId, type: selected.object.type}, action: 'add'}; if(updateSharingApiUrl){ $.ajax(updateSharingApiUrl, { @@ -94,8 +93,7 @@ function yonaIssueSharerModule(findUsersByloginIdsApiUrl, findSharableUsersApiUr }); $issueSharer.on("select2-removing", function(selected) { - var sharer = selected.choice; - var data = { sharer: {loginId: sharer.loginId, type: sharer.type}, action: 'delete'}; + var data = { sharer: {loginId: selected.choice.loginId, type: selected.choice.type}, action: 'delete'}; if(updateSharingApiUrl){ $.ajax(updateSharingApiUrl, { From bc21fb253d1a72be65ffddec66088e7beb477cc4 Mon Sep 17 00:00:00 2001 From: Suwon Chae Date: Fri, 9 Mar 2018 11:52:03 +0900 Subject: [PATCH 05/16] subtask: Refactor before starting next work Extract subtask listing view code into separated partial file. --- .../partial_view_childIssueList.scala.html | 56 +++++++++++++++++++ app/views/issue/view.scala.html | 45 +-------------- 2 files changed, 57 insertions(+), 44 deletions(-) create mode 100644 app/views/issue/partial_view_childIssueList.scala.html 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/view.scala.html b/app/views/issue/view.scala.html index f96b11145..12749c80f 100644 --- a/app/views/issue/view.scala.html +++ b/app/views/issue/view.scala.html @@ -77,45 +77,6 @@ @VOTER_AVATAR_SHOW_LIMIT = @{ 5 } -@showChildIssues(parentIssueId: Long) = { -@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) { - - } -
- } - } -} -} - @conatinsCurrentUserInWatchers = @{Watch.isWatching(UserApp.currentUser(), issue.asResource())} @projectLayout(titleForOGTag, project, utils.MenuType.ISSUE){ @@ -259,11 +220,7 @@
- @if(issue.parent == null) { - @showChildIssues(issue.id) - } else { - @showChildIssues(issue.parent.id) - } + @partial_view_childIssueList(issue, project)
@** Comment **@
From 152f3513306b4ecc3eaabaf8cf265a9f77d0e6a5 Mon Sep 17 00:00:00 2001 From: Suwon Chae Date: Fri, 9 Mar 2018 14:06:58 +0900 Subject: [PATCH 06/16] issue: Gain access of all subtasks when parent issue is shared - If an issue is shared with a user and that issue has subtasks, shared user also can access subtasks. - If a subtask issue is shared with a user, then that user cannot see other subtask list related with. --- app/utils/AccessControl.java | 36 ++++++++++++++------------------- app/views/issue/view.scala.html | 20 ++++++++++++++++++ 2 files changed, 35 insertions(+), 21 deletions(-) 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/view.scala.html b/app/views/issue/view.scala.html index 12749c80f..3320173c9 100644 --- a/app/views/issue/view.scala.html +++ b/app/views/issue/view.scala.html @@ -79,6 +79,20 @@ @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 +} + @projectLayout(titleForOGTag, project, utils.MenuType.ISSUE){ @projectMenu(project, utils.MenuType.ISSUE, "main-menu-only")

@@ -220,7 +234,13 @@
+ @if(amIShared) { + @if(isThisParentIssue) { + @partial_view_childIssueList(issue, project) + } + } else { @partial_view_childIssueList(issue, project) + }
@** Comment **@
From a81aa8ccb158e3976a8f2510d852cf06c1151409 Mon Sep 17 00:00:00 2001 From: Suwon Chae Date: Tue, 13 Mar 2018 11:10:34 +0900 Subject: [PATCH 07/16] auth: Fix error when to be accessed by anonymous In some case, e.g. dual name (English name, Korean name) is used by a user, when anonymous user access, error can be occurred. This commit fix it. --- app/models/User.java | 4 ++++ 1 file changed, 4 insertions(+) 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 { From d46526dd42eef0a7fe09a2e3ef1c4d11ba6eb4c0 Mon Sep 17 00:00:00 2001 From: Suwon Chae Date: Tue, 13 Mar 2018 11:22:14 +0900 Subject: [PATCH 08/16] project: Reduce impact of duplicatd project member It is possible to be duplicated when to add a user as a project member. The best solution is searching and removing all duplicated members. And after that, add unique constraints into project_user table. ``` delete p1 from project_user p1, project_user p2 where p1.id > p2.id and p1.user_id = p2.user_id and p1.project_id = p2.project_id and p1.role_id = p2.role_id; alter table project_user add constraint uq_project_user_1 unique (user_id, project_id, role_id); ``` But more easy way to control this bug is just making it removable from project member. This commit implemented second one. See: Yona Github issue #364 --- app/models/ProjectUser.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) 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) { From dbd108317ac07b1c142f120c1bd2a404d55a4d4b Mon Sep 17 00:00:00 2001 From: Suwon Chae Date: Tue, 13 Mar 2018 11:36:34 +0900 Subject: [PATCH 09/16] noti: Fix missing commit push notificiation bug See: Yona Github issue #369 --- app/models/NotificationEvent.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/NotificationEvent.java b/app/models/NotificationEvent.java index 545dadd87..71cf30359 100644 --- a/app/models/NotificationEvent.java +++ b/app/models/NotificationEvent.java @@ -1011,11 +1011,11 @@ private static Set getMandatoryReceivers(Comment comment, EventType eventT 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; } @@ -1183,7 +1183,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); From db2e13ab2737382d132815a8158a2a93e0ae869e Mon Sep 17 00:00:00 2001 From: Suwon Chae Date: Tue, 13 Mar 2018 17:42:53 +0900 Subject: [PATCH 10/16] ui: Make list sorting filter ui more distinguishable --- app/assets/stylesheets/less/_page.less | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/less/_page.less b/app/assets/stylesheets/less/_page.less index d653d9517..8cc48c740 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; } From 6c40572524c783e68c4f66c8eeec0fd35c286d46 Mon Sep 17 00:00:00 2001 From: Suwon Chae Date: Tue, 13 Mar 2018 19:02:12 +0900 Subject: [PATCH 11/16] issue: Add descriptions to issue share button --- app/assets/stylesheets/less/_page.less | 24 ++++++++++++++++++++++-- app/views/issue/view.scala.html | 4 ++-- conf/messages | 1 + conf/messages.ko-KR | 1 + 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/less/_page.less b/app/assets/stylesheets/less/_page.less index 8cc48c740..0829e92f6 100644 --- a/app/assets/stylesheets/less/_page.less +++ b/app/assets/stylesheets/less/_page.less @@ -7143,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/views/issue/view.scala.html b/app/views/issue/view.scala.html index 3320173c9..e79af0196 100644 --- a/app/views/issue/view.scala.html +++ b/app/views/issue/view.scala.html @@ -168,7 +168,7 @@ } @if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.UPDATE) && !hasSharer) { - + }
@@ -494,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/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 = 전체 From 445f7d3d6a04c0264633746bb140949be1129f90 Mon Sep 17 00:00:00 2001 From: Mijeong Park Date: Sat, 17 Mar 2018 17:58:27 +0900 Subject: [PATCH 12/16] issue: Fix voters bug Voter's names are removed in voter's list in issue and comment It might be caused by modifying original object through subList(). So, we removed calling this method. --- app/controllers/VoteApp.java | 16 ++++++++-------- app/views/issue/partial_comment.scala.html | 2 +- app/views/issue/partial_voters.scala.html | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) 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/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_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) {
  • 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,7 +1023,7 @@ 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; } @@ -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; } From 93acc6a9e6b114d54448a8931ed66fda8e8f315e Mon Sep 17 00:00:00 2001 From: Suwon Chae Date: Tue, 20 Mar 2018 14:16:29 +0900 Subject: [PATCH 14/16] db: Change to lowercase of table name in custom query --- app/models/Project.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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(); } From f432377ca21dc7880dcb2cf73ecf7bfa5388aa07 Mon Sep 17 00:00:00 2001 From: Suwon Chae Date: Wed, 21 Mar 2018 11:39:13 +0900 Subject: [PATCH 15/16] lib: Fix at.js bug related with IME See: https://github.com/ichord/At.js/pull/534 --- public/javascripts/lib/atjs/jquery.atwho.js | 3 +++ 1 file changed, 3 insertions(+) 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 = []; From 547536490021280bc3880dfe2722f16963580a36 Mon Sep 17 00:00:00 2001 From: Mijeong Park Date: Thu, 22 Mar 2018 14:20:10 +0900 Subject: [PATCH 16/16] Modify version --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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,