From 31c6ebb31046470d41a625195662dfccdff116c4 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Lang Date: Thu, 20 Dec 2007 19:10:24 +0000 Subject: [PATCH] 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 --- app/controllers/wiki_controller.rb | 5 +++ app/models/wiki_content.rb | 8 +++++ app/models/wiki_page.rb | 45 +++++++++++++++++++++++++ app/views/wiki/annotate.rhtml | 32 ++++++++++++++++++ app/views/wiki/history.rhtml | 2 ++ lib/redmine.rb | 2 +- public/stylesheets/scm.css | 6 ++++ test/fixtures/wiki_content_versions.yml | 2 +- test/functional/wiki_controller_test.rb | 14 ++++++++ 9 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 app/views/wiki/annotate.rhtml diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index 2ee22167d..bc4b7b451 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -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]) diff --git a/app/models/wiki_content.rb b/app/models/wiki_content.rb index d0a48467b..c307beb1d 100644 --- a/app/models/wiki_content.rb +++ b/app/models/wiki_content.rb @@ -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 diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index cbca4fd68..8ce71cb80 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -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 diff --git a/app/views/wiki/annotate.rhtml b/app/views/wiki/annotate.rhtml new file mode 100644 index 000000000..1c683404b --- /dev/null +++ b/app/views/wiki/annotate.rhtml @@ -0,0 +1,32 @@ +
+<%= 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') %> +
+ +

<%= @page.pretty_title %>

+ +

+<%= l(:label_version) %> <%= link_to @annotate.content.version, :action => 'index', :page => @page.title, :version => @annotate.content.version %> +(<%= @annotate.content.author ? @annotate.content.author.name : "anonyme" %>, <%= format_time(@annotate.content.updated_on) %>) +

+ +<% colors = Hash.new {|k,v| k[v] = (k.size % 12) } %> + + + +<% line_num = 1 %> +<% @annotate.lines.each do |line| -%> + + + + + + +<% line_num += 1 %> +<% end -%> + +
<%= line_num %><%= link_to line[0], :controller => 'wiki', :action => 'index', :id => @project, :page => @page.title, :version => line[0] %><%= h(line[1]) %>
<%= line[2] %>
+ +<% content_for :header_tags do %> +<%= stylesheet_link_tag 'scm' %> +<% end %> diff --git a/app/views/wiki/history.rhtml b/app/views/wiki/history.rhtml index b2a467752..fc77e7fb5 100644 --- a/app/views/wiki/history.rhtml +++ b/app/views/wiki/history.rhtml @@ -11,6 +11,7 @@ <%= l(:field_updated_on) %> <%= l(:field_author) %> <%= l(:field_comments) %> + <% show_diff = @versions.size > 1 %> @@ -23,6 +24,7 @@ <%= format_time(ver.updated_on) %> <%= ver.author ? ver.author.name : "anonyme" %> <%=h ver.comments %> + <%= link_to l(:button_annotate), :action => 'annotate', :page => @page.title, :version => ver.version %> <% line_num += 1 %> <% end %> diff --git a/lib/redmine.rb b/lib/redmine.rb index 921e16b30..9b29257bd 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -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 diff --git a/public/stylesheets/scm.css b/public/stylesheets/scm.css index c3dc307d6..66847af8c 100644 --- a/public/stylesheets/scm.css +++ b/public/stylesheets/scm.css @@ -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; } diff --git a/test/fixtures/wiki_content_versions.yml b/test/fixtures/wiki_content_versions.yml index 547030ccf..260149060 100644 --- a/test/fixtures/wiki_content_versions.yml +++ b/test/fixtures/wiki_content_versions.yml @@ -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: "" diff --git a/test/functional/wiki_controller_test.rb b/test/functional/wiki_controller_test.rb index 043b0e805..0418a02b6 100644 --- a/test/functional/wiki_controller_test.rb +++ b/test/functional/wiki_controller_test.rb @@ -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',