summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorMarin Jankovski <maxlazio@gmail.com>2014-10-06 17:29:06 +0200
committerMarin Jankovski <maxlazio@gmail.com>2014-10-06 17:29:06 +0200
commit0fed1b587630ff98808c61881511ee7c077c3db6 (patch)
tree8b164d6ac47fb33157952094184eb2f529a41a0c /app
parent0c291f35f5324d159155d16aa632fad412a2d3a3 (diff)
parent31bc42de57b3cfd7bf068df06d15372307b8661b (diff)
downloadgitlab-ce-0fed1b587630ff98808c61881511ee7c077c3db6.tar.gz
Merge pull request #7964 from mr-vinn/task-lists
Add task lists to issues and merge requests
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/issue.js.coffee24
-rw-r--r--app/assets/javascripts/merge_request.js.coffee23
-rw-r--r--app/assets/stylesheets/generic/common.scss3
-rw-r--r--app/assets/stylesheets/generic/lists.scss4
-rw-r--r--app/controllers/projects/issues_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests_controller.rb2
-rw-r--r--app/models/concerns/taskable.rb51
-rw-r--r--app/models/issue.rb1
-rw-r--r--app/models/merge_request.rb1
-rw-r--r--app/services/issues/update_service.rb14
-rw-r--r--app/services/merge_requests/update_service.rb8
-rw-r--r--app/views/projects/issues/_issue.html.haml4
-rw-r--r--app/views/projects/issues/show.html.haml2
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml4
-rw-r--r--app/views/projects/merge_requests/show/_mr_box.html.haml2
15 files changed, 138 insertions, 7 deletions
diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee
index 36935a0a159..f2b531fb2b1 100644
--- a/app/assets/javascripts/issue.js.coffee
+++ b/app/assets/javascripts/issue.js.coffee
@@ -6,4 +6,28 @@ class Issue
$(".issue-box .inline-update").on "change", "#issue_assignee_id", ->
$(this).submit()
+ if $("a.btn-close").length
+ $("li.task-list-item input:checkbox").prop("disabled", false)
+
+ $(".task-list-item input:checkbox").on "click", ->
+ is_checked = $(this).prop("checked")
+ if $(this).is(":checked")
+ state_event = "task_check"
+ else
+ state_event = "task_uncheck"
+
+ mr_url = $("form.edit-issue").first().attr("action")
+ mr_num = mr_url.match(/\d+$/)
+ task_num = 0
+ $("li.task-list-item input:checkbox").each( (index, e) =>
+ if e == this
+ task_num = index + 1
+ )
+
+ $.ajax
+ type: "PATCH"
+ url: mr_url
+ data: "issue[state_event]=" + state_event +
+ "&issue[task_num]=" + task_num
+
@Issue = Issue
diff --git a/app/assets/javascripts/merge_request.js.coffee b/app/assets/javascripts/merge_request.js.coffee
index 4c9f20ae6fa..203c721c30c 100644
--- a/app/assets/javascripts/merge_request.js.coffee
+++ b/app/assets/javascripts/merge_request.js.coffee
@@ -17,6 +17,8 @@ class MergeRequest
disableButtonIfEmptyField '#commit_message', '.accept_merge_request'
+ if $("a.close-mr-link").length
+ $("li.task-list-item input:checkbox").prop("disabled", false)
# Local jQuery finder
$: (selector) ->
@@ -72,6 +74,27 @@ class MergeRequest
this.$('.remove_source_branch_in_progress').hide()
this.$('.remove_source_branch_widget.failed').show()
+ this.$(".task-list-item input:checkbox").on "click", ->
+ is_checked = $(this).prop("checked")
+ if $(this).is(":checked")
+ state_event = "task_check"
+ else
+ state_event = "task_uncheck"
+
+ mr_url = $("form.edit-merge_request").first().attr("action")
+ mr_num = mr_url.match(/\d+$/)
+ task_num = 0
+ $("li.task-list-item input:checkbox").each( (index, e) =>
+ if e == this
+ task_num = index + 1
+ )
+
+ $.ajax
+ type: "PATCH"
+ url: mr_url
+ data: "merge_request[state_event]=" + state_event +
+ "&merge_request[task_num]=" + task_num
+
activateTab: (action) ->
this.$('.merge-request-tabs li').removeClass 'active'
this.$('.tab-content').hide()
diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss
index 803219a2e86..cd2f4e45e3c 100644
--- a/app/assets/stylesheets/generic/common.scss
+++ b/app/assets/stylesheets/generic/common.scss
@@ -356,3 +356,6 @@ table {
font-size: 42px;
}
+.task-status {
+ margin-left: 10px;
+}
diff --git a/app/assets/stylesheets/generic/lists.scss b/app/assets/stylesheets/generic/lists.scss
index d347ab2c2e4..2653bfbf831 100644
--- a/app/assets/stylesheets/generic/lists.scss
+++ b/app/assets/stylesheets/generic/lists.scss
@@ -122,3 +122,7 @@ ul.bordered-list {
}
}
}
+
+li.task-list-item {
+ list-style-type: none;
+}
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 9e7a55b23fd..c6d526f05c5 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -152,7 +152,7 @@ class Projects::IssuesController < Projects::ApplicationController
def issue_params
params.require(:issue).permit(
:title, :assignee_id, :position, :description,
- :milestone_id, :state_event, label_ids: []
+ :milestone_id, :state_event, :task_num, label_ids: []
)
end
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index e13773d6465..20a733b10e1 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -250,7 +250,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
params.require(:merge_request).permit(
:title, :assignee_id, :source_project_id, :source_branch,
:target_project_id, :target_branch, :milestone_id,
- :state_event, :description, label_ids: []
+ :state_event, :description, :task_num, label_ids: []
)
end
end
diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb
new file mode 100644
index 00000000000..410e8dc820b
--- /dev/null
+++ b/app/models/concerns/taskable.rb
@@ -0,0 +1,51 @@
+# Contains functionality for objects that can have task lists in their
+# descriptions. Task list items can be added with Markdown like "* [x] Fix
+# bugs".
+#
+# Used by MergeRequest and Issue
+module Taskable
+ TASK_PATTERN_MD = /^(?<bullet> *[*-] *)\[(?<checked>[ xX])\]/.freeze
+ TASK_PATTERN_HTML = /^<li>\[(?<checked>[ xX])\]/.freeze
+
+ # Change the state of a task list item for this Taskable. Edit the object's
+ # description by finding the nth task item and changing its checkbox
+ # placeholder to "[x]" if +checked+ is true, or "[ ]" if it's false.
+ # Note: task numbering starts with 1
+ def update_nth_task(n, checked)
+ index = 0
+ check_char = checked ? 'x' : ' '
+
+ # Do this instead of using #gsub! so that ActiveRecord detects that a field
+ # has changed.
+ self.description = self.description.gsub(TASK_PATTERN_MD) do |match|
+ index += 1
+ case index
+ when n then "#{$LAST_MATCH_INFO[:bullet]}[#{check_char}]"
+ else match
+ end
+ end
+
+ save
+ end
+
+ # Return true if this object's description has any task list items.
+ def tasks?
+ description && description.match(TASK_PATTERN_MD)
+ end
+
+ # Return a string that describes the current state of this Taskable's task
+ # list items, e.g. "20 tasks (12 done, 8 unfinished)"
+ def task_status
+ return nil unless description
+
+ num_tasks = 0
+ num_done = 0
+
+ description.scan(TASK_PATTERN_MD) do
+ num_tasks += 1
+ num_done += 1 unless $LAST_MATCH_INFO[:checked] == ' '
+ end
+
+ "#{num_tasks} tasks (#{num_done} done, #{num_tasks - num_done} unfinished)"
+ end
+end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 13152fdf94e..8a9e969248c 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -23,6 +23,7 @@ require 'file_size_validator'
class Issue < ActiveRecord::Base
include Issuable
include InternalId
+ include Taskable
ActsAsTaggableOn.strict_case_match = true
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index e0358c1889c..7c525b02f48 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -25,6 +25,7 @@ require Rails.root.join("lib/static_model")
class MergeRequest < ActiveRecord::Base
include Issuable
+ include Taskable
include InternalId
belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project"
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index a0e57144435..5b2746ffecf 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -8,9 +8,14 @@ module Issues
Issues::ReopenService.new(project, current_user, {}).execute(issue)
when 'close'
Issues::CloseService.new(project, current_user, {}).execute(issue)
+ when 'task_check'
+ issue.update_nth_task(params[:task_num].to_i, true)
+ when 'task_uncheck'
+ issue.update_nth_task(params[:task_num].to_i, false)
end
- if params.present? && issue.update_attributes(params.except(:state_event))
+ if params.present? && issue.update_attributes(params.except(:state_event,
+ :task_num))
issue.reset_events_cache
if issue.previous_changes.include?('milestone_id')
@@ -28,5 +33,12 @@ module Issues
issue
end
+
+ private
+
+ def update_task(issue, params, checked)
+ issue.update_nth_task(params[:task_num].to_i, checked)
+ params.except!(:task_num)
+ end
end
end
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 6e416a0080c..fc26619cd17 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -17,9 +17,15 @@ module MergeRequests
MergeRequests::ReopenService.new(project, current_user, {}).execute(merge_request)
when 'close'
MergeRequests::CloseService.new(project, current_user, {}).execute(merge_request)
+ when 'task_check'
+ merge_request.update_nth_task(params[:task_num].to_i, true)
+ when 'task_uncheck'
+ merge_request.update_nth_task(params[:task_num].to_i, false)
end
- if params.present? && merge_request.update_attributes(params.except(:state_event))
+ if params.present? && merge_request.update_attributes(
+ params.except(:state_event, :task_num)
+ )
merge_request.reset_events_cache
if merge_request.previous_changes.include?('milestone_id')
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index e089b5fa1cf..b125706781c 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -26,6 +26,10 @@
%span
%i.fa.fa-clock-o
= issue.milestone.title
+ - if issue.tasks?
+ %span.task-status
+ = issue.task_status
+
.pull-right
%small updated #{time_ago_with_tooltip(issue.updated_at, 'bottom', 'issue_update_ago')}
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 4c1ea098d98..e1849b3f8b8 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -48,7 +48,7 @@
.description
.wiki
= preserve do
- = markdown @issue.description
+ = markdown(@issue.description, parse_tasks: true)
.context
%cite.cgray
= render partial: 'issue_context', locals: { issue: @issue }
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 647e8873e9e..1ee2e1bdae8 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -27,7 +27,9 @@
%span
%i.fa.fa-clock-o
= merge_request.milestone.title
-
+ - if merge_request.tasks?
+ %span.task-status
+ = merge_request.task_status
.pull-right
%small updated #{time_ago_with_tooltip(merge_request.updated_at, 'bottom', 'merge_request_updated_ago')}
diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml
index f1aaba2010d..7e5a4eda508 100644
--- a/app/views/projects/merge_requests/show/_mr_box.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_box.html.haml
@@ -18,7 +18,7 @@
.description
.wiki
= preserve do
- = markdown @merge_request.description
+ = markdown(@merge_request.description, parse_tasks: true)
.context
%cite.cgray