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 |