Create a pending task when a user is mentioned on a note

parent 14fc05eb
......@@ -59,6 +59,15 @@
.task-note {
word-wrap: break-word;
.md {
color: #7f8fa4;
font-size: $gl-font-size;
p {
color: #5c5d5e;
}
}
pre {
border: none;
background: #f9f9f9;
......@@ -68,10 +77,25 @@
overflow: hidden;
}
.note-image-attach {
margin-top: 4px;
margin-left: 0px;
max-width: 200px;
float: none;
}
p:last-child {
margin-bottom: 0;
}
}
.task-note-icon {
color: #777;
float: left;
font-size: $gl-font-size;
line-height: 16px;
margin-right: 5px;
}
}
&:last-child { border:none }
......
......@@ -22,4 +22,20 @@ module TasksHelper
[task.action_name, target].join(" ")
end
def task_note_link_html(task)
link_to task_note_target_path(task) do
"##{task.target_iid}"
end
end
def task_note_target_path(task)
polymorphic_path([task.project.namespace.becomes(Namespace),
task.project, task.target], anchor: dom_id(task.note))
end
def task_note(text, options = {})
text = first_line_in_markdown(text, 150, options)
sanitize(text, tags: %w(a img b pre code p span))
end
end
......@@ -37,6 +37,8 @@ class Note < ActiveRecord::Base
belongs_to :author, class_name: "User"
belongs_to :updated_by, class_name: "User"
has_many :tasks, dependent: :delete_all
delegate :name, to: :project, prefix: true
delegate :name, :email, to: :author, prefix: true
......
......@@ -8,6 +8,7 @@
# target_id :integer not null
# target_type :string not null
# author_id :integer
# note_id :integer
# action :integer
# state :string not null
# created_at :datetime
......@@ -19,6 +20,7 @@ class Task < ActiveRecord::Base
MENTIONED = 2
belongs_to :author, class_name: "User"
belongs_to :note
belongs_to :project
belongs_to :target, polymorphic: true, touch: true
belongs_to :user
......@@ -52,6 +54,10 @@ class Task < ActiveRecord::Base
target.respond_to? :title
end
def note_text
note.try(:note)
end
def target_iid
target.respond_to?(:iid) ? target.iid : target_id
end
......
......@@ -77,7 +77,17 @@ class TaskService
def new_note(note)
# Skip system notes, like status changes and cross-references
unless note.system
mark_pending_tasks_as_done(note.noteable, note.author)
project = note.project
target = note.noteable
author = note.author
mark_pending_tasks_as_done(target, author)
mentioned_users = build_mentioned_users(project, note, author)
mentioned_users.each do |user|
create_task(project, target, author, user, Task::MENTIONED, note)
end
end
end
......@@ -94,14 +104,15 @@ class TaskService
private
def create_task(project, target, author, user, action)
def create_task(project, target, author, user, action, note = nil)
attributes = {
project: project,
user_id: user.id,
author_id: author.id,
target_id: target.id,
target_type: target.class.name,
action: action
action: action,
note: note
}
Task.create(attributes)
......
.task-title
%span.author_name= link_to_author task
%span.task_label{class: task.action_name}
= task_action_name(task)
%strong= link_to "##{task.target_iid}", [task.project.namespace.becomes(Namespace), task.project, task.target]
&middot; #{time_ago_with_tooltip(task.created_at)}
- if task.pending?
.task-actions.pull-right
= link_to 'Done', [:dashboard, task], method: :delete, class: 'btn'
- if task.body?
.task-body
.task-note
= task.target.title
.task-title
%span.author_name
= link_to_author task
%span.task_label{class: task.action_name}
= task_action_name(task)
= task_note_link_html(task)
&middot; #{time_ago_with_tooltip(task.created_at)}
- if task.pending?
.task-actions.pull-right
= link_to 'Done', [:dashboard, task], method: :delete, class: 'btn'
.task-body
.task-note
.md
= task_note(task.note_text, project: task.project)
- note = task.note
- if note.attachment.url
- if note.attachment.image?
= link_to note.attachment.url, target: '_blank' do
= image_tag note.attachment.url, class: 'note-image-attach'
- else
= link_to note.attachment.url, target: "_blank", class: 'note-file-attach' do
%i.fa.fa-paperclip
= note.attachment_identifier
......@@ -2,20 +2,7 @@
.task-item{class: "#{task.body? ? 'task-block' : 'task-inline' }"}
= image_tag avatar_icon(task.author_email, 40), class: "avatar s40", alt:''
.task-title
%span.author_name= link_to_author task
%span.task_label{class: task.action_name}
= task_action_name(task)
%strong= link_to "##{task.target_iid}", [task.project.namespace.becomes(Namespace), task.project, task.target]
&middot; #{time_ago_with_tooltip(task.created_at)}
- if task.pending?
.task-actions.pull-right
= link_to 'Done', [:dashboard, task], method: :delete, class: 'btn'
- if task.body?
.task-body
.task-note
= task.target.title
- if task.note.present?
= render 'note', task: task
- else
= render 'common', task: task
class AddNoteToTasks < ActiveRecord::Migration
def change
add_reference :tasks, :note, index: true
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160217100506) do
ActiveRecord::Schema.define(version: 20160217174422) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -834,9 +834,11 @@ ActiveRecord::Schema.define(version: 20160217100506) do
t.string "state", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "note_id"
end
add_index "tasks", ["author_id"], name: "index_tasks_on_author_id", using: :btree
add_index "tasks", ["note_id"], name: "index_tasks_on_note_id", using: :btree
add_index "tasks", ["project_id"], name: "index_tasks_on_project_id", using: :btree
add_index "tasks", ["state"], name: "index_tasks_on_state", using: :btree
add_index "tasks", ["target_type", "target_id"], name: "index_tasks_on_target_type_and_target_id", using: :btree
......
......@@ -26,6 +26,8 @@ describe Note, models: true do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:noteable) }
it { is_expected.to belong_to(:author).class_name('User') }
it { is_expected.to have_many(:tasks).dependent(:delete_all) }
end
describe 'validation' do
......
......@@ -8,6 +8,7 @@
# target_id :integer not null
# target_type :string not null
# author_id :integer
# note_id :integer
# action :integer
# state :string not null
# created_at :datetime
......@@ -19,6 +20,7 @@ require 'spec_helper'
describe Task, models: true do
describe 'relationships' do
it { is_expected.to belong_to(:author).class_name("User") }
it { is_expected.to belong_to(:note) }
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:target).touch(true) }
it { is_expected.to belong_to(:user) }
......@@ -48,6 +50,20 @@ describe Task, models: true do
it 'returns false when target does not respond to title'
end
describe '#note_text' do
it 'returns nil when note is blank' do
subject.note = nil
expect(subject.note_text).to be_nil
end
it 'returns note when note is present' do
subject.note = build(:note, note: 'quick fix')
expect(subject.note_text).to eq 'quick fix'
end
end
describe '#target_iid' do
it 'returns target.iid when target respond to iid'
it 'returns target_id when target does not respond to iid'
......
......@@ -128,6 +128,17 @@ describe TaskService, services: true do
expect(first_pending_task.reload).to be_pending
expect(second_pending_task.reload).to be_pending
end
it 'creates a task for each valid mentioned user' do
note.update_attribute(:note, mentions)
service.new_note(note)
should_create_task(user: michael, target: issue, author: john_doe, action: Task::MENTIONED, note: note)
should_create_task(user: author, target: issue, author: john_doe, action: Task::MENTIONED, note: note)
should_not_create_task(user: john_doe, target: issue, author: john_doe, action: Task::MENTIONED, note: note)
should_not_create_task(user: stranger, target: issue, author: john_doe, action: Task::MENTIONED, note: note)
end
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment