Added wiki annotate view. It's accessible for each version from the page history view.

Slight style change: pre-wrap added on file/diff contents.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@1020 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Jean-Philippe Lang 2007-12-20 19:10:24 +00:00
parent 3d5381b24b
commit 31c6ebb310
9 changed files with 114 additions and 2 deletions

View File

@ -114,6 +114,11 @@ class WikiController < ApplicationController
render_404 unless @diff
end
def annotate
@page = @wiki.find_page(params[:page])
@annotate = @page.annotate(params[:version])
end
# remove a wiki page and its history
def destroy
@page = @wiki.find_page(params[:page])

View File

@ -60,6 +60,14 @@ class WikiContent < ActiveRecord::Base
data
end
end
# Returns the previous version or nil
def previous
@previous ||= WikiContent::Version.find(:first,
:order => 'version DESC',
:include => :author,
:conditions => ["wiki_content_id = ? AND version < ?", wiki_content_id, version])
end
end
end

View File

@ -16,6 +16,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'diff'
require 'enumerator'
class WikiPage < ActiveRecord::Base
belongs_to :wiki
@ -87,6 +88,12 @@ class WikiPage < ActiveRecord::Base
(content_to && content_from) ? WikiDiff.new(content_to, content_from) : nil
end
def annotate(version=nil)
version = version ? version.to_i : self.content.version
c = content.versions.find_by_version(version)
c ? WikiAnnotate.new(c) : nil
end
def self.pretty_title(str)
(str && str.is_a?(String)) ? str.tr('_', ' ') : str
end
@ -113,3 +120,41 @@ class WikiDiff
@diff = words_from.diff @words
end
end
class WikiAnnotate
attr_reader :lines, :content
def initialize(content)
@content = content
current = content
current_lines = current.text.split(/\r?\n/)
@lines = current_lines.collect {|t| [nil, nil, t]}
positions = []
current_lines.size.times {|i| positions << i}
while (current.previous)
d = current.previous.text.split(/\r?\n/).diff(current.text.split(/\r?\n/)).diffs.flatten
d.each_slice(3) do |s|
sign, line = s[0], s[1]
if sign == '+' && positions[line] && positions[line] != -1
if @lines[positions[line]][0].nil?
@lines[positions[line]][0] = current.version
@lines[positions[line]][1] = current.author
end
end
end
d.each_slice(3) do |s|
sign, line = s[0], s[1]
if sign == '-'
positions.insert(line, -1)
else
positions[line] = nil
end
end
positions.compact!
# Stop if every line is annotated
break unless @lines.detect { |line| line[0].nil? }
current = current.previous
end
@lines.each { |line| line[0] ||= current.version }
end
end

View File

@ -0,0 +1,32 @@
<div class="contextual">
<%= link_to(l(:button_edit), {:action => 'edit', :page => @page.title}, :class => 'icon icon-edit') %>
<%= link_to(l(:label_history), {:action => 'history', :page => @page.title}, :class => 'icon icon-history') %>
</div>
<h2><%= @page.pretty_title %></h2>
<p>
<%= l(:label_version) %> <%= link_to @annotate.content.version, :action => 'index', :page => @page.title, :version => @annotate.content.version %>
<em>(<%= @annotate.content.author ? @annotate.content.author.name : "anonyme" %>, <%= format_time(@annotate.content.updated_on) %>)</em>
</p>
<% colors = Hash.new {|k,v| k[v] = (k.size % 12) } %>
<table class="filecontent annotate CodeRay ">
<tbody>
<% line_num = 1 %>
<% @annotate.lines.each do |line| -%>
<tr class="bloc-<%= colors[line[0]] %>">
<th class="line-num"><%= line_num %></th>
<td class="revision"><%= link_to line[0], :controller => 'wiki', :action => 'index', :id => @project, :page => @page.title, :version => line[0] %></td>
<td class="author"><%= h(line[1]) %></td>
<td class="line-code"><pre><%= line[2] %></pre></td>
</tr>
<% line_num += 1 %>
<% end -%>
</tbody>
</table>
<% content_for :header_tags do %>
<%= stylesheet_link_tag 'scm' %>
<% end %>

View File

@ -11,6 +11,7 @@
<th><%= l(:field_updated_on) %></th>
<th><%= l(:field_author) %></th>
<th><%= l(:field_comments) %></th>
<th></th>
</tr></thead>
<tbody>
<% show_diff = @versions.size > 1 %>
@ -23,6 +24,7 @@
<td align="center"><%= format_time(ver.updated_on) %></td>
<td><em><%= ver.author ? ver.author.name : "anonyme" %></em></td>
<td><%=h ver.comments %></td>
<td align="center"><%= link_to l(:button_annotate), :action => 'annotate', :page => @page.title, :version => ver.version %></td>
</tr>
<% line_num += 1 %>
<% end %>

View File

@ -71,7 +71,7 @@ Redmine::AccessControl.map do |map|
map.permission :manage_wiki, {:wikis => [:edit, :destroy]}, :require => :member
map.permission :rename_wiki_pages, {:wiki => :rename}, :require => :member
map.permission :delete_wiki_pages, {:wiki => :destroy}, :require => :member
map.permission :view_wiki_pages, :wiki => [:index, :history, :diff, :special]
map.permission :view_wiki_pages, :wiki => [:index, :history, :diff, :annotate, :special]
map.permission :edit_wiki_pages, :wiki => [:edit, :preview, :add_attachment, :destroy_attachment]
end

View File

@ -10,6 +10,11 @@ table.filecontent th.line-num {
width: 2%;
padding-right: 3px;
}
table.filecontent td.line-code pre {
white-space: pre-wrap; /* CSS2.1 compliant */
white-space: -moz-pre-wrap; /* Mozilla-based browsers */
white-space: -o-pre-wrap; /* Opera 7+ */
}
/* 12 different colors for the annonate view */
table.annotate tr.bloc-0 {background: #FFFFBF;}
@ -40,6 +45,7 @@ table.annotate td.author {
padding-right: 1em;
width: 3%;
background: inherit;
font-size: 90%;
}
table.annotate td.line-code { background-color: #fafafa; }

View File

@ -4,7 +4,7 @@ wiki_content_versions_001:
page_id: 1
id: 1
version: 1
author_id: 1
author_id: 2
comments: Page creation
wiki_content_id: 1
compression: ""

View File

@ -98,6 +98,20 @@ class WikiControllerTest < Test::Unit::TestCase
:content => /updated/
end
def test_annotate
get :annotate, :id => 1, :page => 'CookBook_documentation', :version => 2
assert_response :success
assert_template 'annotate'
# Line 1
assert_tag :tag => 'tr', :child => { :tag => 'th', :attributes => {:class => 'line-num'}, :content => '1' },
:child => { :tag => 'td', :attributes => {:class => 'author'}, :content => /John Smith/ },
:child => { :tag => 'td', :content => /h1\. CookBook documentation/ }
# Line 2
assert_tag :tag => 'tr', :child => { :tag => 'th', :attributes => {:class => 'line-num'}, :content => '2' },
:child => { :tag => 'td', :attributes => {:class => 'author'}, :content => /redMine Admin/ },
:child => { :tag => 'td', :content => /Some updated \[\[documentation\]\] here/ }
end
def test_rename_with_redirect
@request.session[:user_id] = 2
post :rename, :id => 1, :page => 'Another_page',