diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index b61d9875e03e91c6cac4464cd295e6a493efdfc3..6518c380bdcc034c90a9c4b744a17d45cfaa3796 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -21,6 +21,8 @@ class Dispatcher
         Issues.init()
       when 'projects:issues:show'
         new Issue()
+      when 'projects:milestones:show'
+        new Milestone()
       when 'projects:issues:new', 'projects:merge_requests:new'
         GitLab.GfmAutoComplete.setup()
       when 'dashboard:show'
diff --git a/app/assets/javascripts/milestone.js.coffee b/app/assets/javascripts/milestone.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..d2857948cd7a4df97759883af2685de75ba3745c
--- /dev/null
+++ b/app/assets/javascripts/milestone.js.coffee
@@ -0,0 +1,41 @@
+class Milestone
+  @updateIssue: (li, issue_url, data) ->
+    $.ajax
+      type: "PUT"
+      url: issue_url
+      data: data
+      success: (data) ->
+        if data.saved == true
+          $(li).effect 'highlight'
+        else
+          new Flash("Issue update failed", 'alert')
+      dataType: "json"
+
+  constructor: ->
+    @bindSorting()
+
+  bindSorting: ->
+    $("#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed").sortable(
+      connectWith: ".issues-sortable-list",
+      dropOnEmpty: true,
+      receive: (event, ui) ->
+        new_state = $(this).data('state')
+        issue_id = ui.item.data('iid')
+        issue_url = ui.item.data('url')
+
+        data = switch new_state
+          when 'ongoing'
+            "issue[assignee_id]=" + gon.current_user_id
+          when 'unassigned'
+            "issue[assignee_id]="
+          when 'closed'
+            "issue[state_event]=close"
+
+        if $(ui.sender).data('state') == "closed"
+          data += "&issue[state_event]=reopen"
+
+        Milestone.updateIssue(ui.item, issue_url, data)
+
+    ).disableSelection()
+
+@Milestone = Milestone
diff --git a/app/assets/stylesheets/main/mixins.scss b/app/assets/stylesheets/main/mixins.scss
index 289490712b66059149781bdef9ed2c6d6ef9ce7e..cf6d5ba933649a646efc9cf7ca3fc1b75522aec2 100644
--- a/app/assets/stylesheets/main/mixins.scss
+++ b/app/assets/stylesheets/main/mixins.scss
@@ -124,7 +124,7 @@
   margin-bottom: 10px;
 }
 
-@mixin str-truncated($max_width: "82%") {
+@mixin str-truncated($max_width: 82%) {
   display: inline-block;
   overflow: hidden;
   text-overflow: ellipsis;
diff --git a/app/assets/stylesheets/sections/milestone.scss b/app/assets/stylesheets/sections/milestone.scss
new file mode 100644
index 0000000000000000000000000000000000000000..d20391e38fd0cbb627b8514627a4b3ca41081f4e
--- /dev/null
+++ b/app/assets/stylesheets/sections/milestone.scss
@@ -0,0 +1,3 @@
+.issues-sortable-list .str-truncated {
+  max-width: 70%;
+}
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 2730e9942ecfaf4ff861ede955eb915ee9e2947c..aa532de7aa47c30a11086dcbca891527c54c867d 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -174,10 +174,14 @@ class ApplicationController < ActionController::Base
   def add_gon_variables
     gon.default_issues_tracker = Project.issues_tracker.default_value
     gon.api_version = API::API.version
-    gon.api_token = current_user.private_token if current_user
     gon.gravatar_url = request.ssl? || Gitlab.config.gitlab.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url
     gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
     gon.gravatar_enabled = Gitlab.config.gravatar.enabled
+
+    if current_user
+      gon.current_user_id = current_user.id
+      gon.api_token = current_user.private_token
+    end
   end
 
   def check_password_expiration
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 6eec2094f86bb2af55062ff78b1eaf2e4d721bfb..6c6fc7e0779ef92b79053be885ddb83a8cd80225 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -87,6 +87,11 @@ class Projects::IssuesController < Projects::ApplicationController
           render :edit
         end
       end
+      format.json do
+        render json: {
+          saved: @issue.valid?,
+        }
+      end
     end
   end
 
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 713eab5ad7decde97899d50c86b4e10e6e78a0a1..9a227fcef59deb7c97f0c335cd32614eb88dfc7f 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -24,6 +24,8 @@ module Issuable
     scope :unassigned, -> { where("assignee_id IS NULL") }
     scope :of_projects, ->(ids) { where(project_id: ids) }
     scope :opened, -> { with_state(:opened, :reopened) }
+    scope :only_opened, -> { with_state(:opened) }
+    scope :only_reopened, -> { with_state(:reopened) }
     scope :closed, -> { with_state(:closed) }
 
     delegate :name,
diff --git a/app/views/projects/milestones/_issue.html.haml b/app/views/projects/milestones/_issue.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..fe93164a704f88fb2e6e18a6da4ddce1e311807d
--- /dev/null
+++ b/app/views/projects/milestones/_issue.html.haml
@@ -0,0 +1,9 @@
+%li{ class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => project_issue_path(@project, issue) }
+  %span.str-truncated
+    = link_to [@project, issue] do
+      %span.cgray ##{issue.iid}
+    = link_to_gfm issue.title, [@project, issue]
+  - if issue.assignee
+    .pull-right
+      = image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16"
+
diff --git a/app/views/projects/milestones/_issues.html.haml b/app/views/projects/milestones/_issues.html.haml
index 83eb327975b406a7d32d55993c82048ec47f5339..9dbcab19a2a52d5dd4edc35e6fc6b82bea97dc38 100644
--- a/app/views/projects/milestones/_issues.html.haml
+++ b/app/views/projects/milestones/_issues.html.haml
@@ -1,11 +1,6 @@
 .panel.panel-default
   .panel-heading= title
-  %ul.well-list
+  %ul{ class: "well-list issues-sortable-list", id: "issues-list-#{id}", "data-state" => id }
     - issues.each do |issue|
-      %li
-        = link_to [@project, issue] do
-          %span.label{class: issue.closed? ? 'label-danger' : 'label-info'} ##{issue.iid}
-        = link_to_gfm truncate(issue.title, length: 40), [@project, issue]
-        - if issue.assignee
-          .pull-right
-            = image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16"
+      = render 'issue', issue: issue
+    %li.light Drag and drop available
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index d355f25882757ea0ca3586be03af511ce84ea802..67b45fc30ba7125a73c692027ea04ec38b885cf1 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -35,6 +35,12 @@
   %h4.title
     = gfm escape_once(@milestone.title)
 
+  - if @milestone.description.present?
+    .description
+      .wiki
+        = preserve do
+          = markdown @milestone.description
+
   .context
     %p
       Progress:
@@ -45,11 +51,6 @@
     .progress.progress-info
       .progress-bar{style: "width: #{@milestone.percent_complete}%;"}
 
-  - if @milestone.description.present?
-    .description
-      .wiki
-        = preserve do
-          = markdown @milestone.description
 
 %ul.nav.nav-tabs
   %li.active
@@ -75,11 +76,11 @@
   .tab-pane.active#tab-issues
     .row
       .col-md-4
-        = render('issues', title: 'Unstarted Issues (open and unassigned)', issues: @issues.opened.unassigned)
+        = render('issues', title: 'Unstarted Issues (open and unassigned)', issues: @issues.opened.unassigned, id: 'unassigned')
       .col-md-4
-        = render('issues', title: 'Ongoing Issues (open and assigned)', issues: @issues.opened.assigned)
+        = render('issues', title: 'Ongoing Issues (open and assigned)', issues: @issues.opened.assigned, id: 'ongoing')
       .col-md-4
-        = render('issues', title: 'Completed Issues (closed)', issues: @issues.closed)
+        = render('issues', title: 'Completed Issues (closed)', issues: @issues.closed, id: 'closed')
 
   .tab-pane#tab-merge-requests
     .row
diff --git a/features/steps/project/milestones.rb b/features/steps/project/milestones.rb
index 9ce18fbaabd99971d184bd5891c7cfb3b213a76f..5562b523d1b3dc776777daca657744cd7924b013 100644
--- a/features/steps/project/milestones.rb
+++ b/features/steps/project/milestones.rb
@@ -54,6 +54,6 @@ class ProjectMilestones < Spinach::FeatureSteps
   end
 
   Then "I should see 3 issues" do
-    page.should have_selector('#tab-issues li', count: 4)
+    page.should have_selector('#tab-issues li.issue-row', count: 4)
   end
 end