Commit 36bd6c84 authored by Douwe Maan's avatar Douwe Maan

Show who last edited a comment if it wasn't the original author

parent a06827bf
...@@ -17,6 +17,7 @@ v 7.14.0 (unreleased) ...@@ -17,6 +17,7 @@ v 7.14.0 (unreleased)
- Add fetch command to the MR page - Add fetch command to the MR page
- Fix bug causing Bitbucket importer to crash when OAuth application had been removed. - Fix bug causing Bitbucket importer to crash when OAuth application had been removed.
- Add fetch command to the MR page. - Add fetch command to the MR page.
- Show who last edited a comment if it wasn't the original author
v 7.13.2 v 7.13.2
- Fix randomly failed spec - Fix randomly failed spec
......
...@@ -30,13 +30,10 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -30,13 +30,10 @@ class Projects::NotesController < Projects::ApplicationController
end end
def update def update
if note.editable? @note = Notes::UpdateService.new(project, current_user, note_params).execute(note)
note.update_attributes(note_params)
note.reset_events_cache
end
respond_to do |format| respond_to do |format|
format.json { render_note_json(note) } format.json { render_note_json(@note) }
format.html { redirect_to :back } format.html { redirect_to :back }
end end
end end
......
...@@ -43,21 +43,6 @@ module IssuesHelper ...@@ -43,21 +43,6 @@ module IssuesHelper
end end
end end
def issue_timestamp(issue)
# Shows the created at time and the updated at time if different
ts = time_ago_with_tooltip(issue.created_at, placement: 'bottom', html_class: 'note_created_ago')
if issue.updated_at != issue.created_at
ts << capture_haml do
haml_tag :span do
haml_concat '&middot;'
haml_concat icon('edit', title: 'edited')
haml_concat time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_edited_ago')
end
end
end
ts.html_safe
end
def bulk_update_milestone_options def bulk_update_milestone_options
options_for_select([['None (backlog)', -1]]) + options_for_select([['None (backlog)', -1]]) +
options_from_collection_for_select(project_active_milestones, 'id', options_from_collection_for_select(project_active_milestones, 'id',
......
...@@ -23,21 +23,6 @@ module NotesHelper ...@@ -23,21 +23,6 @@ module NotesHelper
end end
end end
def note_timestamp(note)
# Shows the created at time and the updated at time if different
ts = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note_created_ago')
if note.updated_at != note.created_at
ts << capture_haml do
haml_tag :span do
haml_concat '&middot;'
haml_concat icon('edit', title: 'edited')
haml_concat time_ago_with_tooltip(note.updated_at, placement: 'bottom', html_class: 'note_edited_ago')
end
end
end
ts.html_safe
end
def noteable_json(noteable) def noteable_json(noteable)
{ {
id: noteable.id, id: noteable.id,
......
...@@ -21,7 +21,7 @@ module ProjectsHelper ...@@ -21,7 +21,7 @@ module ProjectsHelper
end end
def link_to_member(project, author, opts = {}) def link_to_member(project, author, opts = {})
default_opts = { avatar: true, name: true, size: 16 } default_opts = { avatar: true, name: true, size: 16, author_class: 'author' }
opts = default_opts.merge(opts) opts = default_opts.merge(opts)
return "(deleted)" unless author return "(deleted)" unless author
...@@ -32,7 +32,7 @@ module ProjectsHelper ...@@ -32,7 +32,7 @@ module ProjectsHelper
author_html << image_tag(avatar_icon(author.try(:email), opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] author_html << image_tag(avatar_icon(author.try(:email), opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar]
# Build name span tag # Build name span tag
author_html << content_tag(:span, sanitize(author.name), class: 'author') if opts[:name] author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name]
author_html = author_html.html_safe author_html = author_html.html_safe
......
...@@ -12,6 +12,7 @@ module Issuable ...@@ -12,6 +12,7 @@ module Issuable
included do included do
belongs_to :author, class_name: "User" belongs_to :author, class_name: "User"
belongs_to :assignee, class_name: "User" belongs_to :assignee, class_name: "User"
belongs_to :updated_by, class_name: "User"
belongs_to :milestone belongs_to :milestone
has_many :notes, as: :noteable, dependent: :destroy has_many :notes, as: :noteable, dependent: :destroy
has_many :label_links, as: :target, dependent: :destroy has_many :label_links, as: :target, dependent: :destroy
......
...@@ -33,6 +33,7 @@ class Note < ActiveRecord::Base ...@@ -33,6 +33,7 @@ class Note < ActiveRecord::Base
belongs_to :project belongs_to :project
belongs_to :noteable, polymorphic: true, touch: true belongs_to :noteable, polymorphic: true, touch: true
belongs_to :author, class_name: "User" belongs_to :author, class_name: "User"
belongs_to :updated_by, class_name: "User"
delegate :name, to: :project, prefix: true delegate :name, to: :project, prefix: true
delegate :name, :email, to: :author, prefix: true delegate :name, :email, to: :author, prefix: true
......
...@@ -14,7 +14,7 @@ module Issues ...@@ -14,7 +14,7 @@ module Issues
filter_params filter_params
old_labels = issue.labels.to_a old_labels = issue.labels.to_a
if params.present? && issue.update_attributes(params) if params.present? && issue.update_attributes(params.merge(updated_by: current_user))
issue.reset_events_cache issue.reset_events_cache
if issue.labels != old_labels if issue.labels != old_labels
......
...@@ -24,7 +24,7 @@ module MergeRequests ...@@ -24,7 +24,7 @@ module MergeRequests
filter_params filter_params
old_labels = merge_request.labels.to_a old_labels = merge_request.labels.to_a
if params.present? && merge_request.update_attributes(params) if params.present? && merge_request.update_attributes(params.merge(updated_by: current_user))
merge_request.reset_events_cache merge_request.reset_events_cache
if merge_request.labels != old_labels if merge_request.labels != old_labels
......
module Notes module Notes
class UpdateService < BaseService class UpdateService < BaseService
def execute def execute(note)
note = project.notes.find(params[:note_id]) return note unless note.editable?
note.note = params[:note]
if note.save
notification_service.new_note(note)
# Skip system notes, like status changes and cross-references. note.update_attributes(params.merge(updated_by: current_user))
unless note.system
event_service.leave_note(note, note.author)
# Create a cross-reference note if this Note contains GFM that project.reset_events_cache
# names an issue, merge request, or commit.
note.references.each do |mentioned|
SystemNoteService.cross_reference(mentioned, note.noteable, note.author)
end
end
end
note note
end end
......
...@@ -9,7 +9,13 @@ ...@@ -9,7 +9,13 @@
Open Open
Issue ##{@issue.iid} Issue ##{@issue.iid}
%small.creator %small.creator
&middot; created by #{link_to_member(@project, @issue.author)} #{issue_timestamp(@issue)} &middot; created by #{link_to_member(@project, @issue.author)}
= time_ago_with_tooltip(@issue.created_at, placement: 'bottom', html_class: 'issue_created_ago')
- if @issue.updated_at != @issue.created_at
%span
&middot;
= icon('edit', title: 'edited')
= time_ago_with_tooltip(@issue.updated_at, placement: 'bottom', html_class: 'issue_edited_ago')
.pull-right .pull-right
- if can?(current_user, :create_issue, @project) - if can?(current_user, :create_issue, @project)
......
%h4.page-title %h4.page-title
.issue-box{ class: issue_box_class(@merge_request) } .issue-box{ class: issue_box_class(@merge_request) }
= @merge_request.state_human_name = @merge_request.state_human_name
= "Merge Request ##{@merge_request.iid}" Merge Request ##{@merge_request.iid}
%small.creator %small.creator
&middot; &middot;
created by #{link_to_member(@project, @merge_request.author)} #{time_ago_with_tooltip(@merge_request.created_at)} created by #{link_to_member(@project, @merge_request.author)}
= time_ago_with_tooltip(@merge_request.created_at)
- if @merge_request.updated_at != @merge_request.created_at
%span
&middot;
= icon('edit', title: 'edited')
= time_ago_with_tooltip(@merge_request.updated_at, placement: 'bottom')
.issue-btn-group.pull-right .issue-btn-group.pull-right
- if can?(current_user, :update_merge_request, @merge_request) - if can?(current_user, :update_merge_request, @merge_request)
......
...@@ -33,7 +33,14 @@ ...@@ -33,7 +33,14 @@
%span.note-last-update %span.note-last-update
= link_to "##{dom_id(note)}", name: dom_id(note), title: "Link here" do = link_to "##{dom_id(note)}", name: dom_id(note), title: "Link here" do
= note_timestamp(note) = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note_created_ago')
- if note.updated_at != note.created_at
%span
&middot;
= icon('edit', title: 'edited')
= time_ago_with_tooltip(note.updated_at, placement: 'bottom', html_class: 'note_edited_ago')
- if note.updated_by && note.updated_by != note.author
by #{link_to_member(note.project, note.updated_by, avatar: false, author_class: nil)}
- if note.superceded?(@notes) - if note.superceded?(@notes)
- if note.upvote? - if note.upvote?
......
class AddUpdatedByToIssuablesAndNotes < ActiveRecord::Migration
def change
add_column :notes, :updated_by_id, :integer
add_column :issues, :updated_by_id, :integer
add_column :merge_requests, :updated_by_id, :integer
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150717130904) do ActiveRecord::Schema.define(version: 20150730122406) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -128,12 +128,13 @@ ActiveRecord::Schema.define(version: 20150717130904) do ...@@ -128,12 +128,13 @@ ActiveRecord::Schema.define(version: 20150717130904) do
t.integer "project_id" t.integer "project_id"
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
t.integer "position", default: 0 t.integer "position", default: 0
t.string "branch_name" t.string "branch_name"
t.text "description" t.text "description"
t.integer "milestone_id" t.integer "milestone_id"
t.string "state" t.string "state"
t.integer "iid" t.integer "iid"
t.integer "updated_by_id"
end end
add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
...@@ -230,6 +231,7 @@ ActiveRecord::Schema.define(version: 20150717130904) do ...@@ -230,6 +231,7 @@ ActiveRecord::Schema.define(version: 20150717130904) do
t.text "description" t.text "description"
t.integer "position", default: 0 t.integer "position", default: 0
t.datetime "locked_at" t.datetime "locked_at"
t.integer "updated_by_id"
end end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
...@@ -289,6 +291,7 @@ ActiveRecord::Schema.define(version: 20150717130904) do ...@@ -289,6 +291,7 @@ ActiveRecord::Schema.define(version: 20150717130904) do
t.integer "noteable_id" t.integer "noteable_id"
t.boolean "system", default: false, null: false t.boolean "system", default: false, null: false
t.text "st_diff" t.text "st_diff"
t.integer "updated_by_id"
end end
add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree
......
...@@ -78,17 +78,15 @@ module API ...@@ -78,17 +78,15 @@ module API
put ":id/#{noteables_str}/:#{noteable_id_str}/notes/:note_id" do put ":id/#{noteables_str}/:#{noteable_id_str}/notes/:note_id" do
required_attributes! [:body] required_attributes! [:body]
authorize! :admin_note, user_project.notes.find(params[:note_id]) note = user_project.notes.find(params[:note_id])
authorize! :admin_note, note
opts = { opts = {
note: params[:body], note: params[:body]
note_id: params[:note_id],
noteable_type: noteables_str.classify,
noteable_id: params[noteable_id_str]
} }
@note = ::Notes::UpdateService.new(user_project, current_user, @note = ::Notes::UpdateService.new(user_project, current_user, opts).execute(note)
opts).execute
if @note.valid? if @note.valid?
present @note, with: Entities::Note present @note, with: Entities::Note
......
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