doortts doortts 2017-08-07
issue: Suppor issue creation at any page
@5dfac4deeb095a07db077579f410c56ed474889e
app/assets/stylesheets/less/_page.less
--- app/assets/stylesheets/less/_page.less
+++ app/assets/stylesheets/less/_page.less
@@ -6708,6 +6708,11 @@
     display: inline-block;
 }
 
+.width25px {
+    width: 25px;
+    display: inline-block;
+}
+
 #simple-issue-list {
     .state-label {
         font-size: 13px;
@@ -6921,3 +6926,7 @@
     color: rgba(0, 0, 0, 0.7);
     padding: 0 2px;
 }
+
+.no-margin {
+    margin: 0;
+}
app/controllers/IssueApp.java
--- app/controllers/IssueApp.java
+++ app/controllers/IssueApp.java
@@ -17,6 +17,7 @@
 import models.enumeration.Operation;
 import models.enumeration.ResourceType;
 import models.enumeration.State;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.tika.Tika;
 import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -162,6 +163,7 @@
         if(project == null){
             return ok(my_list.render("title.issueList", issues, searchCondition, project));
         } else {
+            UserApp.currentUser().visits(project);
             return ok(list.render("title.issueList", issues, searchCondition, project));
         }
 
@@ -281,6 +283,54 @@
         return ok(partial_comments.render(project, issueInfo));
     }
 
+    public static Result newDirectIssueForm() {
+        User current = UserApp.currentUser();
+
+        Project project = null;
+        // Fallback #1: Favorite projects 1st if exists
+        if(!CollectionUtils.isEmpty(current.favoriteProjects)) {
+            project = current.favoriteProjects.get(current.favoriteProjects.size() - 1).project;
+        }
+
+        // Fallback #2: Last visited project
+        List<Project> visitedProjects = current.getVisitedProjects();
+        if (project == null && !CollectionUtils.isEmpty(visitedProjects)) {
+            project = visitedProjects.get(0);
+        }
+
+        if(project != null){
+            return newIssueForm(project.owner, project.name);
+        } else {
+            flash(Constants.WARNING, Messages.get("project.is.empty"));
+            return Application.index();
+        }
+    }
+
+    public static Result newDirectMyIssueForm() {
+        User current = UserApp.currentUser();
+
+        // Prefixed project. inbox or _private
+        Project project = Project.findByOwnerAndProjectName(current.loginId, "inbox");
+        if( project == null ) {
+            project = Project.findByOwnerAndProjectName(current.loginId, "_private");
+        }
+
+        // Fallback to any other project
+        if( project == null ) {
+            List<Project> projects = Project.findProjectsByMember(current.id);
+            if(!CollectionUtils.isEmpty(projects)) {
+                project = projects.get(0);
+            }
+        }
+
+        if(project != null){
+            return newIssueForm(project.owner, project.name);
+        } else {
+            flash(Constants.WARNING, Messages.get("project.is.empty"));
+            return Application.index();
+        }
+    }
+
     @AnonymousCheck(requiresLogin = true, displaysFlashMessage = true)
     @IsCreatable(ResourceType.ISSUE_POST)
     public static Result newIssueForm(String ownerName, String projectName) {
app/models/RecentProject.java
--- app/models/RecentProject.java
+++ app/models/RecentProject.java
@@ -84,7 +84,6 @@
         if(existed != null){
             existed.delete();
         }
-
     }
 
     private static void deleteOldestIfOverflow(User user) {
app/models/User.java
--- app/models/User.java
+++ app/models/User.java
@@ -976,6 +976,15 @@
         projects.addAll(Project.findProjectsByMember(id));
         List<Project> list = new ArrayList<>();
         list.addAll(projects);
+        Collections.sort(list, new Comparator<Project>() {
+            @Override
+            public int compare(Project lhs, Project rhs) {
+                if(lhs.owner.compareToIgnoreCase(rhs.owner) == 0) {
+                    return lhs.name.compareToIgnoreCase(rhs.name);
+                }
+                return lhs.owner.compareToIgnoreCase(rhs.owner);
+            }
+        });
         return list;
     }
 
app/views/common/select2.scala.html
--- app/views/common/select2.scala.html
+++ app/views/common/select2.scala.html
@@ -34,12 +34,14 @@
 <script id="tplSelect2Projects" type="text/x-jquery-tmpl">
 <div class="usf-group" title="${name}">
     <span class="avatar-wrap smaller"><img src="${avatarURL}" width="16" height="16"></span>
+    <span class="loginid">${owner}</span>
     <span class="name">${name}</span>
 </div>
 </script>
 <script id="tplSelect2ProjectsWithoutAvatar" type="text/x-jquery-tmpl">
 <div class="usf-group" title="${name}">
-    <span class="width16px"></span>
+    <span class="width25px"></span>
+    <span class="loginid">${owner}</span>
     <span class="name">${name}</span>
 </div>
 </script>
app/views/common/usermenu.scala.html
--- app/views/common/usermenu.scala.html
+++ app/views/common/usermenu.scala.html
@@ -103,6 +103,19 @@
           </a>
           <ul class="dropdown-menu flat right">
               <li>
+                  <a href="@routes.IssueApp.newDirectIssueForm()">
+                  @Messages("issue.menu.new")
+                  </a>
+              </li>
+              <li>
+                  <a href="@routes.IssueApp.newDirectMyIssueForm()">
+                  @Messages("issue.menu.new.mine")
+                  </a>
+              </li>
+              <li>
+                  <hr class="no-margin"/>
+              </li>
+              <li>
                   <a href="@routes.ProjectApp.newProjectForm()">
                   @Messages("button.newProject")
                   </a>
app/views/issue/create.scala.html
--- app/views/issue/create.scala.html
+++ app/views/issue/create.scala.html
@@ -19,6 +19,11 @@
     play.mvc.Http.Context.current().request().getQueryString("parentIssueId")
 }
 
+@isFromGlobalMenuNew = @{
+    routes.IssueApp.newDirectIssueForm.toString().equals(requestHeader.path) ||
+            routes.IssueApp.newDirectMyIssueForm.toString().equals(requestHeader.path)
+}
+
 @projectLayout(Messages(title), project, utils.MenuType.ISSUE) {
 @projectMenu(project, utils.MenuType.ISSUE, "main-menu-only")
 <div class="page-wrap-outer">
@@ -157,6 +162,10 @@
             "target": 'textarea[id^=editor-]',
             "url"   : "@routes.ProjectApp.mentionList(project.owner, project.name)"
         });
+
+        if(@isFromGlobalMenuNew) {
+            $(".subtask-message").trigger("click");
+        }
     });
 </script>
 }
app/views/issue/partial_select_subtask.scala.html
--- app/views/issue/partial_select_subtask.scala.html
+++ app/views/issue/partial_select_subtask.scala.html
@@ -16,7 +16,7 @@
             <option value="@project.id" data-avatar-url="@urlToProjectLogo(project)">@project.name</option>
             @for(movableProject <- UserApp.currentUser().getIssueMovableProject) {
                 @if(movableProject.id != project.id) {
-                    <option value="@movableProject.id" data-avatar-url="@urlToProjectLogo(movableProject)">@movableProject.name</option>
+                    <option value="@movableProject.id" data-owner="@movableProject.owner /" data-avatar-url="@urlToProjectLogo(movableProject)">@movableProject.name</option>
                 }
             }
         </select>
conf/messages
--- conf/messages
+++ conf/messages
@@ -289,6 +289,7 @@
 issue.list.commentedByMe = Commented
 issue.list.mentionedOfMe = Mentioned
 issue.menu.new = New issue
+issue.menu.new.mine = New issue - personal
 issue.myIssue = My issues
 issue.new.result = Results
 issue.noAssignee = No assignee
conf/messages.ko-KR
--- conf/messages.ko-KR
+++ conf/messages.ko-KR
@@ -290,6 +290,7 @@
 issue.list.commentedByMe = 댓글 남긴 이슈
 issue.list.mentionedOfMe = 나를 언급한 이슈
 issue.menu.new = 새 이슈
+issue.menu.new.mine = 새 이슈 - 개인
 issue.myIssue = 내 이슈
 issue.new.result = 확인결과
 issue.noAssignee = 담당자 없음
conf/routes
--- conf/routes
+++ conf/routes
@@ -95,6 +95,8 @@
 # User
 GET            /user/issues                                                           controllers.IssueApp.userIssuesPage()
 GET            /user/issues                                                           controllers.IssueApp.userIssues(state:String ?= "", format:String ?= "html", pageNum: Int ?= 1)
+GET            /user/issues/new                                                       controllers.IssueApp.newDirectIssueForm()
+GET            /user/issues/new/mine                                                  controllers.IssueApp.newDirectMyIssueForm()
 GET            /user/files                                                            controllers.UserApp.userFiles()
 GET            /users/loginform                                                       controllers.UserApp.loginForm()
 GET            /users/login                                                           controllers.Application.index()
public/javascripts/common/yobi.ui.Select2.js
--- public/javascripts/common/yobi.ui.Select2.js
+++ public/javascripts/common/yobi.ui.Select2.js
@@ -50,14 +50,17 @@
                     return !avatarURL || avatarURL.indexOf("project_default_logo.png") !== -1;
                 }
 
+                var owner = itemElement.data("owner") ? itemElement.data("owner") : "";
                 if(_doesntHaveProjectAvatar()){
                     return $.tmpl($("#tplSelect2ProjectsWithoutAvatar").text(), {
-                        "name"     : itemObject.text
+                        "name"     : itemObject.text,
+                        "owner"    : owner
                     });
                 } else {
                     return $.tmpl($("#tplSelect2Projects").text(), {
                         "avatarURL": avatarURL,
-                        "name"     : itemObject.text.trim()
+                        "name"     : itemObject.text.trim(),
+                        "owner"    : owner
                     });
                 }
             },
@@ -176,6 +179,15 @@
                 loginId = (typeof loginId !== "undefined") ? loginId.toLowerCase() : "";
 
                 return (loginId.indexOf(term) > -1) || (formattedResult.indexOf(term) > -1);
+            },
+            "projects": function(term, formattedResult, itemElement){
+              term = term.toLowerCase();
+              formattedResult = formattedResult.toLowerCase();
+
+              var owner = itemElement.data("owner") + "";
+              owner = (typeof owner !== "undefined") ? owner.toLowerCase() : "";
+
+              return (owner.indexOf(term) > -1) || (formattedResult.indexOf(term) > -1);
             }
         };
 
public/javascripts/common/yona.Subtask.js
--- public/javascripts/common/yona.Subtask.js
+++ public/javascripts/common/yona.Subtask.js
@@ -20,10 +20,12 @@
         var targetProjectName = selected.target.selectedOptions[0].innerText;
         if(selected.val === initialProjectId){
             parentId.prop("disabled", false);
+            $('#s2id_parentId').show();
             parentId.trigger('change.select2');
         } else {
             parentId.val(parentId.find("option:first").val());
             parentId.prop("disabled", true);
+            $('#s2id_parentId').hide();
             parentId.trigger('change.select2');
             $yobi.notify("Issue will be move/write to '" + targetProjectName + "'", 3000);
         }
Add a comment
List