Feature #2876 » wiki_concurrent_edition.patch
| app/controllers/wiki_controller.rb Sun Jun 28 14:12:27 2009 +0200 → app/controllers/wiki_controller.rb Tue Jun 30 20:21:43 2009 +0200 | ||
|---|---|---|
| 89 | 89 |
end |
| 90 | 90 |
rescue ActiveRecord::StaleObjectError |
| 91 | 91 |
# Optimistic locking exception |
| 92 |
flash[:error] = l(:notice_locking_conflict) |
|
| 92 |
# Try to merge |
|
| 93 |
if @content.merge |
|
| 94 |
flash[:notice] = l(:notice_merged) |
|
| 95 |
redirect_to :action => 'index', :id => @project, :page => @page.title |
|
| 96 |
else |
|
| 97 |
flash[:error] = l(:notice_locking_conflict) |
|
| 98 |
end |
|
| 93 | 99 |
end |
| 94 | 100 |
|
| 95 | 101 |
# rename a page |
| app/models/wiki_content.rb Sun Jun 28 14:12:27 2009 +0200 → app/models/wiki_content.rb Tue Jun 30 20:21:43 2009 +0200 | ||
|---|---|---|
| 92 | 92 |
:conditions => ["wiki_content_id = ? AND version < ?", wiki_content_id, version]) |
| 93 | 93 |
end |
| 94 | 94 |
end |
| 95 | ||
| 96 |
def merge |
|
| 97 |
transaction do |
|
| 98 |
parent_version = versions.find_by_version(version) |
|
| 99 |
last_version = versions.last |
|
| 100 |
begin |
|
| 101 |
conflict, merged_text = Merger.merge(text, parent_version.text, last_version.text, |
|
| 102 |
l(:label_merge_my), |
|
| 103 |
l(:label_merge_old), |
|
| 104 |
l(:label_merge_other, |
|
| 105 |
:author_name => last_version.author.name)) |
|
| 106 |
self.text = merged_text |
|
| 107 |
self.version = last_version.version |
|
| 108 |
return (!conflict and save) |
|
| 109 |
rescue Merger::DiffBinaryNotAvailable |
|
| 110 |
return false |
|
| 111 |
end |
|
| 112 |
end |
|
| 113 |
end |
|
| 114 | ||
| 95 | 115 |
end |
| config/locales/en.yml Sun Jun 28 14:12:27 2009 +0200 → config/locales/en.yml Tue Jun 30 20:21:43 2009 +0200 | ||
|---|---|---|
| 121 | 121 |
notice_successful_connection: Successful connection. |
| 122 | 122 |
notice_file_not_found: The page you were trying to access doesn't exist or has been removed. |
| 123 | 123 |
notice_locking_conflict: Data has been updated by another user. |
| 124 |
notice_merged: "Another user edited this page in the meantime. An automatic merge occured." |
|
| 124 | 125 |
notice_not_authorized: You are not authorized to access this page. |
| 125 | 126 |
notice_email_sent: "An email was sent to {{value}}"
|
| 126 | 127 |
notice_email_error: "An error occurred while sending mail ({{value}})"
|
| ... | ... | |
| 678 | 679 |
label_date_from_to: From {{start}} to {{end}}
|
| 679 | 680 |
label_wiki_content_added: Wiki page added |
| 680 | 681 |
label_wiki_content_updated: Wiki page updated |
| 682 |
label_merge_my: "My version" |
|
| 683 |
label_merge_old: "Previous version" |
|
| 684 |
label_merge_other: "{{author_name}}'s version"
|
|
| 681 | 685 |
|
| 682 | 686 |
button_login: Login |
| 683 | 687 |
button_submit: Submit |
| config/locales/fr.yml Sun Jun 28 14:12:27 2009 +0200 → config/locales/fr.yml Tue Jun 30 20:21:43 2009 +0200 | ||
|---|---|---|
| 153 | 153 |
notice_successful_connection: Connection réussie. |
| 154 | 154 |
notice_file_not_found: "La page à laquelle vous souhaitez accéder n'existe pas ou a été supprimée." |
| 155 | 155 |
notice_locking_conflict: Les données ont été mises à jour par un autre utilisateur. Mise à jour impossible. |
| 156 |
notice_merged: "Un autre utilisateur a modifié la page en même temps que vous. Un assemblage automatique a donc eu lieu." |
|
| 156 | 157 |
notice_not_authorized: "Vous n'êtes pas autorisés à accéder à cette page." |
| 157 | 158 |
notice_email_sent: "Un email a été envoyé à {{value}}"
|
| 158 | 159 |
notice_email_error: "Erreur lors de l'envoi de l'email ({{value}})"
|
| ... | ... | |
| 708 | 709 |
label_date_from_to: Du {{start}} au {{end}}
|
| 709 | 710 |
label_wiki_content_added: Page wiki ajoutée |
| 710 | 711 |
label_wiki_content_updated: Page wiki mise à jour |
| 712 |
label_merge_my: "Ma version" |
|
| 713 |
label_merge_old: "Version précédente" |
|
| 714 |
label_merge_other: "Version de {{author_name}}"
|
|
| 711 | 715 |
|
| 712 | 716 |
button_login: Connexion |
| 713 | 717 |
button_submit: Soumettre |
| /dev/null Thu Jan 01 00:00:00 1970 +0000 → lib/merger.rb Tue Jun 30 20:21:43 2009 +0200 | ||
|---|---|---|
| 1 |
module Merger |
|
| 2 |
|
|
| 3 |
class DiffBinaryNotAvailable < Exception |
|
| 4 |
end |
|
| 5 | ||
| 6 | ||
| 7 |
class << self |
|
| 8 |
private |
|
| 9 |
|
|
| 10 |
def shell_quote(str) |
|
| 11 |
if RUBY_PLATFORM =~ /mswin/ |
|
| 12 |
'"' + str.gsub(/"/, '\\"') + '"' |
|
| 13 |
else |
|
| 14 |
"'" + str.gsub(/'/, "'\"'\"'") + "'" |
|
| 15 |
end |
|
| 16 |
end |
|
| 17 |
|
|
| 18 |
def diff3_command(*opts) |
|
| 19 |
command = format("diff3 %s",
|
|
| 20 |
opts.join(" "))
|
|
| 21 |
output = nil |
|
| 22 |
IO.popen(command) do |io| |
|
| 23 |
output = io.read |
|
| 24 |
end |
|
| 25 |
raise "diff3 exited with an error" if $?.exitstatus > 1 |
|
| 26 |
return output |
|
| 27 |
end |
|
| 28 |
|
|
| 29 |
def tempfile(content, &block) |
|
| 30 |
Tempfile.open("redmine_wiki_merge") do |t_file|
|
|
| 31 |
t_file << content |
|
| 32 |
t_file.open |
|
| 33 |
yield t_file |
|
| 34 |
end |
|
| 35 |
end |
|
| 36 |
end |
|
| 37 |
|
|
| 38 |
begin |
|
| 39 |
diff3_command('-v').blank?
|
|
| 40 |
def self.merge(mine, old, other, mine_label, old_label, other_label) |
|
| 41 |
tempfile(mine) do |my_file| |
|
| 42 |
tempfile(old) do |old_file| |
|
| 43 |
tempfile(other) do |other_file| |
|
| 44 |
conflict = diff3_command("-a", "-x", shell_quote(my_file.path),
|
|
| 45 |
shell_quote(old_file.path), |
|
| 46 |
shell_quote(other_file.path)) |
|
| 47 |
return !conflict.blank?, diff3_command("-a", "-m", "-E",
|
|
| 48 |
"-L #{shell_quote(mine_label)}",
|
|
| 49 |
"-L #{shell_quote(old_label)}",
|
|
| 50 |
"-L #{shell_quote(other_label)}",
|
|
| 51 |
shell_quote(my_file.path), |
|
| 52 |
shell_quote(old_file.path), |
|
| 53 |
shell_quote(other_file.path)) |
|
| 54 |
end |
|
| 55 |
end |
|
| 56 |
end |
|
| 57 |
end |
|
| 58 |
rescue |
|
| 59 |
def self.merge(*args) |
|
| 60 |
raise "diff3 binary not found." |
|
| 61 |
end |
|
| 62 |
end |
|
| 63 |
end |
|
| /dev/null Thu Jan 01 00:00:00 1970 +0000 → test/unit/merger_test.rb Tue Jun 30 20:21:43 2009 +0200 | ||
|---|---|---|
| 1 |
require File.dirname(__FILE__) + '/../test_helper' |
|
| 2 | ||
| 3 |
class MergerTest < Test::Unit::TestCase |
|
| 4 | ||
| 5 |
def test_simple_merge |
|
| 6 |
assert_equal [false, "aa\n\nbb\n"], Merger.merge("a\n\nbb\n", "a\n\nb\n", "aa\n\nb\n",
|
|
| 7 |
"my", "old", "yours") |
|
| 8 |
end |
|
| 9 | ||
| 10 |
end |
|