##// END OF EJS Templates
Wiki: allows single section edit (#2222)....
Jean-Philippe Lang -
r7709:6fc245327ce5
parent child
Show More
@@ -100,6 +100,13 class WikiController < ApplicationController
100
100
101 # To prevent StaleObjectError exception when reverting to a previous version
101 # To prevent StaleObjectError exception when reverting to a previous version
102 @content.version = @page.content.version
102 @content.version = @page.content.version
103
104 @text = @content.text
105 if params[:section].present?
106 @section = params[:section].to_i
107 @text, @section_hash = Redmine::WikiFormatting.formatter.new(@text).get_section(@section)
108 render_404 if @text.blank?
109 end
103 end
110 end
104
111
105 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
112 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
@@ -120,7 +127,17 class WikiController < ApplicationController
120 redirect_to :action => 'show', :project_id => @project, :id => @page.title
127 redirect_to :action => 'show', :project_id => @project, :id => @page.title
121 return
128 return
122 end
129 end
123 @content.attributes = params[:content]
130
131 @content.comments = params[:content][:comments]
132 @text = params[:content][:text]
133 if params[:section].present?
134 @section = params[:section].to_i
135 @section_hash = params[:section_hash]
136 @content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(params[:section].to_i, @text, @section_hash)
137 else
138 @content.version = params[:content][:version]
139 @content.text = @text
140 end
124 @content.author = User.current
141 @content.author = User.current
125 # if page is new @page.save will also save content, but not if page isn't a new record
142 # if page is new @page.save will also save content, but not if page isn't a new record
126 if (@page.new_record? ? @page.save : @content.save)
143 if (@page.new_record? ? @page.save : @content.save)
@@ -132,7 +149,7 class WikiController < ApplicationController
132 render :action => 'edit'
149 render :action => 'edit'
133 end
150 end
134
151
135 rescue ActiveRecord::StaleObjectError
152 rescue ActiveRecord::StaleObjectError, Redmine::WikiFormatting::StaleSectionError
136 # Optimistic locking exception
153 # Optimistic locking exception
137 flash.now[:error] = l(:notice_locking_conflict)
154 flash.now[:error] = l(:notice_locking_conflict)
138 render :action => 'edit'
155 render :action => 'edit'
@@ -486,11 +486,11 module ApplicationHelper
486 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
486 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
487 only_path = options.delete(:only_path) == false ? false : true
487 only_path = options.delete(:only_path) == false ? false : true
488
488
489 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr) { |macro, args| exec_macro(macro, obj, args) }
489 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text, :object => obj, :attribute => attr)
490
490
491 @parsed_headings = []
491 @parsed_headings = []
492 text = parse_non_pre_blocks(text) do |text|
492 text = parse_non_pre_blocks(text) do |text|
493 [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings].each do |method_name|
493 [:parse_sections, :parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_macros, :parse_headings].each do |method_name|
494 send method_name, text, project, obj, attr, only_path, options
494 send method_name, text, project, obj, attr, only_path, options
495 end
495 end
496 end
496 end
@@ -728,7 +728,23 module ApplicationHelper
728 end
728 end
729 end
729 end
730
730
731 HEADING_RE = /<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>/i unless const_defined?(:HEADING_RE)
731 HEADING_RE = /(<h(1|2|3|4)( [^>]+)?>(.+?)<\/h(1|2|3|4)>)/i unless const_defined?(:HEADING_RE)
732
733 def parse_sections(text, project, obj, attr, only_path, options)
734 return unless options[:edit_section_links]
735 section = 0
736 text.gsub!(HEADING_RE) do
737 section += 1
738 if section > 1
739 content_tag('div',
740 link_to(image_tag('edit.png'), options[:edit_section_links].merge(:section => section)),
741 :class => 'contextual',
742 :title => l(:button_edit_section)) + $1
743 else
744 $1
745 end
746 end
747 end
732
748
733 # Headings and TOC
749 # Headings and TOC
734 # Adds ids and links to headings unless options[:headings] is set to false
750 # Adds ids and links to headings unless options[:headings] is set to false
@@ -736,7 +752,7 module ApplicationHelper
736 return if options[:headings] == false
752 return if options[:headings] == false
737
753
738 text.gsub!(HEADING_RE) do
754 text.gsub!(HEADING_RE) do
739 level, attrs, content = $1.to_i, $2, $3
755 level, attrs, content = $2.to_i, $3, $4
740 item = strip_tags(content).strip
756 item = strip_tags(content).strip
741 anchor = sanitize_anchor_name(item)
757 anchor = sanitize_anchor_name(item)
742 # used for single-file wiki export
758 # used for single-file wiki export
@@ -746,6 +762,33 module ApplicationHelper
746 end
762 end
747 end
763 end
748
764
765 MACROS_RE = /
766 (!)? # escaping
767 (
768 \{\{ # opening tag
769 ([\w]+) # macro name
770 (\(([^\}]*)\))? # optional arguments
771 \}\} # closing tag
772 )
773 /x unless const_defined?(:MACROS_RE)
774
775 # Macros substitution
776 def parse_macros(text, project, obj, attr, only_path, options)
777 text.gsub!(MACROS_RE) do
778 esc, all, macro = $1, $2, $3.downcase
779 args = ($5 || '').split(',').each(&:strip)
780 if esc.nil?
781 begin
782 exec_macro(macro, obj, args)
783 rescue => e
784 "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
785 end || all
786 else
787 all
788 end
789 end
790 end
791
749 TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
792 TOC_RE = /<p>\{\{([<>]?)toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
750
793
751 # Renders the TOC with given headings
794 # Renders the TOC with given headings
@@ -1,3 +1,4
1 <div class="wiki">
1 <div class="wiki wiki-page">
2 <%= textilizable content, :text, :attachments => content.page.attachments %>
2 <%= textilizable content, :text, :attachments => content.page.attachments,
3 :edit_section_links => (content.is_a?(WikiContent) && @editable && User.current.allowed_to?(:edit_wiki_pages, @page.project) && {:controller => 'wiki', :action => 'edit', :project_id => @page.project, :id => @page.title}) %>
3 </div>
4 </div>
@@ -4,9 +4,13
4
4
5 <% form_for :content, @content, :url => {:action => 'update', :id => @page.title}, :html => {:method => :put, :multipart => true, :id => 'wiki_form'} do |f| %>
5 <% form_for :content, @content, :url => {:action => 'update', :id => @page.title}, :html => {:method => :put, :multipart => true, :id => 'wiki_form'} do |f| %>
6 <%= f.hidden_field :version %>
6 <%= f.hidden_field :version %>
7 <% if @section %>
8 <%= hidden_field_tag 'section', @section %>
9 <%= hidden_field_tag 'section_hash', @section_hash %>
10 <% end %>
7 <%= error_messages_for 'content' %>
11 <%= error_messages_for 'content' %>
8
12
9 <p><%= f.text_area :text, :cols => 100, :rows => 25, :class => 'wiki-edit', :accesskey => accesskey(:edit) %></p>
13 <p><%= text_area_tag 'content[text]', @text, :cols => 100, :rows => 25, :class => 'wiki-edit', :accesskey => accesskey(:edit) %></p>
10 <p><label><%= l(:field_comments) %></label><br /><%= f.text_field :comments, :size => 120 %></p>
14 <p><label><%= l(:field_comments) %></label><br /><%= f.text_field :comments, :size => 120 %></p>
11 <p><label><%=l(:label_attachment_plural)%></label><br /><%= render :partial => 'attachments/form' %></p>
15 <p><label><%=l(:label_attachment_plural)%></label><br /><%= render :partial => 'attachments/form' %></p>
12
16
@@ -999,3 +999,4 bg:
999 description_date_range_interval: Изберете диапазон чрез задаване на начална и крайна дати
999 description_date_range_interval: Изберете диапазон чрез задаване на начална и крайна дати
1000 description_date_from: Въведете начална дата
1000 description_date_from: Въведете начална дата
1001 description_date_to: Въведете крайна дата
1001 description_date_to: Въведете крайна дата
1002 button_edit_section: Edit this section
@@ -1015,3 +1015,4 bs:
1015 label_child_revision: Child
1015 label_child_revision: Child
1016 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1016 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1017 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1017 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1018 button_edit_section: Edit this section
@@ -1004,3 +1004,4 ca:
1004 label_child_revision: Child
1004 label_child_revision: Child
1005 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1005 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1006 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1006 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1007 button_edit_section: Edit this section
@@ -1005,3 +1005,4 cs:
1005 label_child_revision: Child
1005 label_child_revision: Child
1006 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1006 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1007 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1007 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1008 button_edit_section: Edit this section
@@ -1018,3 +1018,4 da:
1018 label_child_revision: Child
1018 label_child_revision: Child
1019 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1019 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1020 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1020 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1021 button_edit_section: Edit this section
@@ -1022,3 +1022,4 de:
1022 label_child_revision: Child
1022 label_child_revision: Child
1023 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1023 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1024 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1024 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1025 button_edit_section: Edit this section
@@ -1001,3 +1001,4 el:
1001 label_child_revision: Child
1001 label_child_revision: Child
1002 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1002 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1003 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1003 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1004 button_edit_section: Edit this section
@@ -1004,3 +1004,4 en-GB:
1004 description_selected_columns: Selected Columns
1004 description_selected_columns: Selected Columns
1005 label_parent_revision: Parent
1005 label_parent_revision: Parent
1006 label_child_revision: Child
1006 label_child_revision: Child
1007 button_edit_section: Edit this section
@@ -874,6 +874,7 en:
874 button_quote: Quote
874 button_quote: Quote
875 button_duplicate: Duplicate
875 button_duplicate: Duplicate
876 button_show: Show
876 button_show: Show
877 button_edit_section: Edit this section
877
878
878 status_active: active
879 status_active: active
879 status_registered: registered
880 status_registered: registered
@@ -1038,3 +1038,4 es:
1038 label_parent_revision: Parent
1038 label_parent_revision: Parent
1039 label_child_revision: Child
1039 label_child_revision: Child
1040 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1040 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1041 button_edit_section: Edit this section
@@ -1005,3 +1005,4 eu:
1005 label_child_revision: Child
1005 label_child_revision: Child
1006 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1006 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1007 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1007 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1008 button_edit_section: Edit this section
@@ -1004,3 +1004,4 fa:
1004 label_child_revision: Child
1004 label_child_revision: Child
1005 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1005 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1006 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1006 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1007 button_edit_section: Edit this section
@@ -1022,3 +1022,4 fi:
1022 label_child_revision: Child
1022 label_child_revision: Child
1023 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1023 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1024 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1024 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1025 button_edit_section: Edit this section
@@ -850,6 +850,7 fr:
850 button_quote: Citer
850 button_quote: Citer
851 button_duplicate: Dupliquer
851 button_duplicate: Dupliquer
852 button_show: Afficher
852 button_show: Afficher
853 button_edit_section: Modifier cette section
853
854
854 status_active: actif
855 status_active: actif
855 status_registered: enregistré
856 status_registered: enregistré
@@ -1013,3 +1013,4 gl:
1013 label_child_revision: Child
1013 label_child_revision: Child
1014 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1014 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1015 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1015 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1016 button_edit_section: Edit this section
@@ -1006,3 +1006,4 he:
1006 label_child_revision: Child
1006 label_child_revision: Child
1007 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1007 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1008 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1008 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1009 button_edit_section: Edit this section
@@ -1008,3 +1008,4 hr:
1008 label_child_revision: Child
1008 label_child_revision: Child
1009 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1009 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1010 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1010 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1011 button_edit_section: Edit this section
@@ -1020,3 +1020,4
1020 label_child_revision: Child
1020 label_child_revision: Child
1021 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1021 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1022 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1022 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1023 button_edit_section: Edit this section
@@ -1009,3 +1009,4 id:
1009 label_child_revision: Child
1009 label_child_revision: Child
1010 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1010 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1011 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1011 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1012 button_edit_section: Edit this section
@@ -1002,3 +1002,4 it:
1002 label_child_revision: Child
1002 label_child_revision: Child
1003 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1003 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1004 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1004 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1005 button_edit_section: Edit this section
@@ -1031,3 +1031,4 ja:
1031 description_selected_columns: Selected Columns
1031 description_selected_columns: Selected Columns
1032 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1032 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1033 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1033 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1034 button_edit_section: Edit this section
@@ -1053,3 +1053,4 ko:
1053 label_child_revision: Child
1053 label_child_revision: Child
1054 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1054 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1055 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1055 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1056 button_edit_section: Edit this section
@@ -1061,3 +1061,4 lt:
1061 label_child_revision: Child
1061 label_child_revision: Child
1062 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1062 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1063 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1063 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1064 button_edit_section: Edit this section
@@ -996,3 +996,4 lv:
996 label_child_revision: Child
996 label_child_revision: Child
997 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
997 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
998 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
998 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
999 button_edit_section: Edit this section
@@ -1001,3 +1001,4 mk:
1001 label_child_revision: Child
1001 label_child_revision: Child
1002 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1002 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1003 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1003 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1004 button_edit_section: Edit this section
@@ -1002,3 +1002,4 mn:
1002 label_child_revision: Child
1002 label_child_revision: Child
1003 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1003 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1004 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1004 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1005 button_edit_section: Edit this section
@@ -983,3 +983,4 nl:
983 label_child_revision: Child
983 label_child_revision: Child
984 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
984 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
985 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
985 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
986 button_edit_section: Edit this section
@@ -991,3 +991,4
991 label_child_revision: Child
991 label_child_revision: Child
992 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
992 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
993 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
993 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
994 button_edit_section: Edit this section
@@ -1018,3 +1018,4 pl:
1018 label_child_revision: Child
1018 label_child_revision: Child
1019 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1019 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1020 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1020 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1021 button_edit_section: Edit this section
@@ -1022,3 +1022,4 pt-BR:
1022 label_child_revision: Child
1022 label_child_revision: Child
1023 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1023 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1024 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1024 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1025 button_edit_section: Edit this section
@@ -1006,3 +1006,4 pt:
1006 label_child_revision: Child
1006 label_child_revision: Child
1007 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1007 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1008 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1008 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1009 button_edit_section: Edit this section
@@ -994,3 +994,4 ro:
994 label_child_revision: Child
994 label_child_revision: Child
995 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
995 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
996 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
996 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
997 button_edit_section: Edit this section
@@ -1114,3 +1114,4 ru:
1114 label_child_revision: Child
1114 label_child_revision: Child
1115 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1115 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1116 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1116 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1117 button_edit_section: Edit this section
@@ -996,3 +996,4 sk:
996 label_child_revision: Child
996 label_child_revision: Child
997 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
997 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
998 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
998 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
999 button_edit_section: Edit this section
@@ -1001,3 +1001,4 sl:
1001 label_child_revision: Child
1001 label_child_revision: Child
1002 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1002 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1003 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1003 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1004 button_edit_section: Edit this section
@@ -1001,3 +1001,4 sr-YU:
1001 label_child_revision: Child
1001 label_child_revision: Child
1002 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1002 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1003 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1003 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1004 button_edit_section: Edit this section
@@ -1002,3 +1002,4 sr:
1002 label_child_revision: Child
1002 label_child_revision: Child
1003 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1003 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1004 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1004 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1005 button_edit_section: Edit this section
@@ -1042,3 +1042,4 sv:
1042 label_child_revision: Child
1042 label_child_revision: Child
1043 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1043 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1044 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1044 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1045 button_edit_section: Edit this section
@@ -998,3 +998,4 th:
998 label_child_revision: Child
998 label_child_revision: Child
999 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
999 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1000 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1000 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1001 button_edit_section: Edit this section
@@ -1020,3 +1020,4 tr:
1020 label_child_revision: Child
1020 label_child_revision: Child
1021 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1021 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1022 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1022 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1023 button_edit_section: Edit this section
@@ -997,3 +997,4 uk:
997 label_child_revision: Child
997 label_child_revision: Child
998 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
998 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
999 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
999 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1000 button_edit_section: Edit this section
@@ -1052,3 +1052,4 vi:
1052 label_child_revision: Child
1052 label_child_revision: Child
1053 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1053 error_scm_annotate_big_text_file: The entry cannot be annotated, as it exceeds the maximum text file size.
1054 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1054 setting_default_issue_start_date_to_creation_date: Use current date as start date for new issues
1055 button_edit_section: Edit this section
@@ -1081,3 +1081,4
1081 description_date_range_interval: 選擇起始與結束日期以設定範圍區間
1081 description_date_range_interval: 選擇起始與結束日期以設定範圍區間
1082 description_date_from: 輸入起始日期
1082 description_date_from: 輸入起始日期
1083 description_date_to: 輸入結束日期
1083 description_date_to: 輸入結束日期
1084 button_edit_section: Edit this section
@@ -1003,3 +1003,4 zh:
1003 label_child_revision: 子修订
1003 label_child_revision: 子修订
1004 error_scm_annotate_big_text_file: 输入文本内容超长,无法输入。
1004 error_scm_annotate_big_text_file: 输入文本内容超长,无法输入。
1005 setting_default_issue_start_date_to_creation_date: 使用当前日期作为新问题的开始日期
1005 setting_default_issue_start_date_to_creation_date: 使用当前日期作为新问题的开始日期
1006 button_edit_section: Edit this section
@@ -17,6 +17,8
17
17
18 module Redmine
18 module Redmine
19 module WikiFormatting
19 module WikiFormatting
20 class StaleSectionError < Exception; end
21
20 @@formatters = {}
22 @@formatters = {}
21
23
22 class << self
24 class << self
@@ -29,6 +31,10 module Redmine
29 @@formatters[name.to_s] = {:formatter => formatter, :helper => helper}
31 @@formatters[name.to_s] = {:formatter => formatter, :helper => helper}
30 end
32 end
31
33
34 def formatter
35 formatter_for(Setting.text_formatting)
36 end
37
32 def formatter_for(name)
38 def formatter_for(name)
33 entry = @@formatters[name.to_s]
39 entry = @@formatters[name.to_s]
34 (entry && entry[:formatter]) || Redmine::WikiFormatting::NullFormatter::Formatter
40 (entry && entry[:formatter]) || Redmine::WikiFormatting::NullFormatter::Formatter
@@ -43,7 +49,7 module Redmine
43 @@formatters.keys.map
49 @@formatters.keys.map
44 end
50 end
45
51
46 def to_html(format, text, options = {}, &block)
52 def to_html(format, text, options = {})
47 text = if Setting.cache_formatted_text? && text.size > 2.kilobyte && cache_store && cache_key = cache_key_for(format, options[:object], options[:attribute])
53 text = if Setting.cache_formatted_text? && text.size > 2.kilobyte && cache_store && cache_key = cache_key_for(format, options[:object], options[:attribute])
48 # Text retrieved from the cache store may be frozen
54 # Text retrieved from the cache store may be frozen
49 # We need to dup it so we can do in-place substitutions with gsub!
55 # We need to dup it so we can do in-place substitutions with gsub!
@@ -53,9 +59,6 module Redmine
53 else
59 else
54 formatter_for(format).new(text).to_html
60 formatter_for(format).new(text).to_html
55 end
61 end
56 if block_given?
57 execute_macros(text, block)
58 end
59 text
62 text
60 end
63 end
61
64
@@ -70,33 +73,6 module Redmine
70 def cache_store
73 def cache_store
71 ActionController::Base.cache_store
74 ActionController::Base.cache_store
72 end
75 end
73
74 MACROS_RE = /
75 (!)? # escaping
76 (
77 \{\{ # opening tag
78 ([\w]+) # macro name
79 (\(([^\}]*)\))? # optional arguments
80 \}\} # closing tag
81 )
82 /x unless const_defined?(:MACROS_RE)
83
84 # Macros substitution
85 def execute_macros(text, macros_runner)
86 text.gsub!(MACROS_RE) do
87 esc, all, macro = $1, $2, $3.downcase
88 args = ($5 || '').split(',').each(&:strip)
89 if esc.nil?
90 begin
91 macros_runner.call(macro, args)
92 rescue => e
93 "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
94 end || all
95 else
96 all
97 end
98 end
99 end
100 end
76 end
101
77
102 # Default formatter module
78 # Default formatter module
@@ -16,6 +16,7
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'redcloth3'
18 require 'redcloth3'
19 require 'digest/md5'
19
20
20 module Redmine
21 module Redmine
21 module WikiFormatting
22 module WikiFormatting
@@ -38,6 +39,68 module Redmine
38 super(*RULES).to_s
39 super(*RULES).to_s
39 end
40 end
40
41
42 def get_section(index)
43 section = extract_sections(index)[1]
44 hash = Digest::MD5.hexdigest(section)
45 return section, hash
46 end
47
48 def update_section(index, update, hash=nil)
49 t = extract_sections(index)
50 if hash.present? && hash != Digest::MD5.hexdigest(t[1])
51 raise Redmine::WikiFormatting::StaleSectionError
52 end
53 t[1] = update unless t[1].blank?
54 t.reject(&:blank?).join "\n\n"
55 end
56
57 def extract_sections(index)
58 @pre_list = []
59 text = self.dup
60 rip_offtags text
61 before = ''
62 s = ''
63 after = ''
64 i = 0
65 l = 1
66 started = false
67 ended = false
68 text.scan(/(((?:.*?)(\A|\r?\n\r?\n))(h(\d+)(#{A}#{C})\.(?::(\S+))? (.*?)$)|.*)/m).each do |all, content, lf, heading, level|
69 if heading.nil?
70 if ended
71 after << all
72 elsif started
73 s << all
74 else
75 before << all
76 end
77 break
78 end
79 i += 1
80 if ended
81 after << all
82 elsif i == index
83 l = level.to_i
84 before << content
85 s << heading
86 started = true
87 elsif i > index
88 s << content
89 if level.to_i > l
90 s << heading
91 else
92 after << heading
93 ended = true
94 end
95 else
96 before << all
97 end
98 end
99 sections = [before.strip, s.strip, after.strip]
100 sections.each {|section| smooth_offtags section}
101 sections
102 end
103
41 private
104 private
42
105
43 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
106 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
@@ -358,6 +358,9 table#time-report tbody tr.last-level { font-style: normal; color: #555; }
358 table#time-report tbody tr.total { font-style: normal; font-weight: bold; color: #555; background-color:#EEEEEE; }
358 table#time-report tbody tr.total { font-style: normal; font-weight: bold; color: #555; background-color:#EEEEEE; }
359 table#time-report .hours-dec { font-size: 0.9em; }
359 table#time-report .hours-dec { font-size: 0.9em; }
360
360
361 div.wiki-page .contextual a {opacity: 0.4}
362 div.wiki-page .contextual a:hover {opacity: 1}
363
361 form .attributes { margin-bottom: 8px; }
364 form .attributes { margin-bottom: 8px; }
362 form .attributes p { padding-top: 1px; padding-bottom: 2px; }
365 form .attributes p { padding-top: 1px; padding-bottom: 2px; }
363 form .attributes select { width: 60%; }
366 form .attributes select { width: 60%; }
@@ -103,4 +103,25 wiki_contents_010:
103 version: 1
103 version: 1
104 author_id: 1
104 author_id: 1
105 comments:
105 comments:
106 No newline at end of file
106 wiki_contents_011:
107 text: |-
108 h1. Title
109
110 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
111
112 h2. Heading 1
113
114 Maecenas sed elit sit amet mi accumsan vestibulum non nec velit. Proin porta tincidunt lorem, consequat rhoncus dolor fermentum in.
115
116 Cras ipsum felis, ultrices at porttitor vel, faucibus eu nunc.
117
118 h2. Heading 2
119
120 Morbi facilisis accumsan orci non pharetra.
121 updated_on: 2007-03-08 00:18:07 +01:00
122 page_id: 11
123 id: 11
124 version: 3
125 author_id: 1
126 comments:
127
@@ -69,3 +69,10 wiki_pages_010:
69 wiki_id: 1
69 wiki_id: 1
70 protected: false
70 protected: false
71 parent_id:
71 parent_id:
72 wiki_pages_011:
73 created_on: 2007-03-08 00:18:07 +01:00
74 title: Page_with_sections
75 id: 11
76 wiki_id: 1
77 protected: false
78 parent_id:
@@ -118,6 +118,44 class WikiControllerTest < ActionController::TestCase
118 assert_equal 'testfile.txt', page.attachments.first.filename
118 assert_equal 'testfile.txt', page.attachments.first.filename
119 end
119 end
120
120
121 def test_edit_page
122 @request.session[:user_id] = 2
123 get :edit, :project_id => 'ecookbook', :id => 'Another_page'
124
125 assert_response :success
126 assert_template 'edit'
127
128 assert_tag 'textarea',
129 :attributes => { :name => 'content[text]' },
130 :content => WikiPage.find_by_title('Another_page').content.text
131 end
132
133 def test_edit_section
134 @request.session[:user_id] = 2
135 get :edit, :project_id => 'ecookbook', :id => 'Page_with_sections', :section => 2
136
137 assert_response :success
138 assert_template 'edit'
139
140 page = WikiPage.find_by_title('Page_with_sections')
141 section, hash = Redmine::WikiFormatting::Textile::Formatter.new(page.content.text).get_section(2)
142
143 assert_tag 'textarea',
144 :attributes => { :name => 'content[text]' },
145 :content => section
146 assert_tag 'input',
147 :attributes => { :name => 'section', :type => 'hidden', :value => '2' }
148 assert_tag 'input',
149 :attributes => { :name => 'section_hash', :type => 'hidden', :value => hash }
150 end
151
152 def test_edit_invalid_section_should_respond_with_404
153 @request.session[:user_id] = 2
154 get :edit, :project_id => 'ecookbook', :id => 'Page_with_sections', :section => 10
155
156 assert_response 404
157 end
158
121 def test_update_page
159 def test_update_page
122 @request.session[:user_id] = 2
160 @request.session[:user_id] = 2
123 assert_no_difference 'WikiPage.count' do
161 assert_no_difference 'WikiPage.count' do
@@ -200,6 +238,83 class WikiControllerTest < ActionController::TestCase
200 assert_equal 2, c.version
238 assert_equal 2, c.version
201 end
239 end
202
240
241 def test_update_section
242 @request.session[:user_id] = 2
243 page = WikiPage.find_by_title('Page_with_sections')
244 section, hash = Redmine::WikiFormatting::Textile::Formatter.new(page.content.text).get_section(2)
245 text = page.content.text
246
247 assert_no_difference 'WikiPage.count' do
248 assert_no_difference 'WikiContent.count' do
249 assert_difference 'WikiContent::Version.count' do
250 put :update, :project_id => 1, :id => 'Page_with_sections',
251 :content => {
252 :text => "New section content",
253 :version => 3
254 },
255 :section => 2,
256 :section_hash => hash
257 end
258 end
259 end
260 assert_redirected_to '/projects/ecookbook/wiki/Page_with_sections'
261 assert_equal Redmine::WikiFormatting::Textile::Formatter.new(text).update_section(2, "New section content"), page.reload.content.text
262 end
263
264 def test_update_section_should_allow_stale_page_update
265 @request.session[:user_id] = 2
266 page = WikiPage.find_by_title('Page_with_sections')
267 section, hash = Redmine::WikiFormatting::Textile::Formatter.new(page.content.text).get_section(2)
268 text = page.content.text
269
270 assert_no_difference 'WikiPage.count' do
271 assert_no_difference 'WikiContent.count' do
272 assert_difference 'WikiContent::Version.count' do
273 put :update, :project_id => 1, :id => 'Page_with_sections',
274 :content => {
275 :text => "New section content",
276 :version => 2 # Current version is 3
277 },
278 :section => 2,
279 :section_hash => hash
280 end
281 end
282 end
283 assert_redirected_to '/projects/ecookbook/wiki/Page_with_sections'
284 page.reload
285 assert_equal Redmine::WikiFormatting::Textile::Formatter.new(text).update_section(2, "New section content"), page.content.text
286 assert_equal 4, page.content.version
287 end
288
289 def test_update_section_should_not_allow_stale_section_update
290 @request.session[:user_id] = 2
291
292 assert_no_difference 'WikiPage.count' do
293 assert_no_difference 'WikiContent.count' do
294 assert_no_difference 'WikiContent::Version.count' do
295 put :update, :project_id => 1, :id => 'Page_with_sections',
296 :content => {
297 :comments => 'My comments',
298 :text => "Text should not be lost",
299 :version => 3
300 },
301 :section => 2,
302 :section_hash => Digest::MD5.hexdigest("wrong hash")
303 end
304 end
305 end
306 assert_response :success
307 assert_template 'edit'
308 assert_tag :div,
309 :attributes => { :class => /error/ },
310 :content => /Data has been updated by another user/
311 assert_tag 'textarea',
312 :attributes => { :name => 'content[text]' },
313 :content => /Text should not be lost/
314 assert_tag 'input',
315 :attributes => { :name => 'content[comments]', :value => 'My comments' }
316 end
317
203 def test_preview
318 def test_preview
204 @request.session[:user_id] = 2
319 @request.session[:user_id] = 2
205 xhr :post, :preview, :project_id => 1, :id => 'CookBook_documentation',
320 xhr :post, :preview, :project_id => 1, :id => 'CookBook_documentation',
@@ -16,6 +16,7
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../../../test_helper', __FILE__)
18 require File.expand_path('../../../../../test_helper', __FILE__)
19 require 'digest/md5'
19
20
20 class Redmine::WikiFormatting::TextileFormatterTest < ActionView::TestCase
21 class Redmine::WikiFormatting::TextileFormatterTest < ActionView::TestCase
21
22
@@ -203,6 +204,101 EXPECTED
203 expected = '<p><img src="/images/comment.png&quot;onclick=&amp;#x61;&amp;#x6c;&amp;#x65;&amp;#x72;&amp;#x74;&amp;#x28;&amp;#x27;&amp;#x58;&amp;#x53;&amp;#x53;&amp;#x27;&amp;#x29;;&amp;#x22;" alt="" /></p>'
204 expected = '<p><img src="/images/comment.png&quot;onclick=&amp;#x61;&amp;#x6c;&amp;#x65;&amp;#x72;&amp;#x74;&amp;#x28;&amp;#x27;&amp;#x58;&amp;#x53;&amp;#x53;&amp;#x27;&amp;#x29;;&amp;#x22;" alt="" /></p>'
204 assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
205 assert_equal expected.gsub(%r{\s+}, ''), to_html(raw).gsub(%r{\s+}, '')
205 end
206 end
207
208
209 STR_WITHOUT_PRE = [
210 # 0
211 "h1. Title
212
213 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.",
214 # 1
215 "h2. Heading 2
216
217 Maecenas sed elit sit amet mi accumsan vestibulum non nec velit. Proin porta tincidunt lorem, consequat rhoncus dolor fermentum in.
218
219 Cras ipsum felis, ultrices at porttitor vel, faucibus eu nunc.",
220 # 2
221 "h2. Heading 2
222
223 Morbi facilisis accumsan orci non pharetra.
224
225 h3. Heading 3
226
227 Nulla nunc nisi, egestas in ornare vel, posuere ac libero.",
228 # 3
229 "h3. Heading 3
230
231 Praesent eget turpis nibh, a lacinia nulla.",
232 # 4
233 "h2. Heading 2
234
235 Ut rhoncus elementum adipiscing."]
236
237 TEXT_WITHOUT_PRE = STR_WITHOUT_PRE.join("\n\n").freeze
238
239 def test_get_section_should_return_the_requested_section_and_its_hash
240 assert_section_with_hash STR_WITHOUT_PRE[1], TEXT_WITHOUT_PRE, 2
241 assert_section_with_hash STR_WITHOUT_PRE[2..3].join("\n\n"), TEXT_WITHOUT_PRE, 3
242 assert_section_with_hash STR_WITHOUT_PRE[3], TEXT_WITHOUT_PRE, 5
243 assert_section_with_hash STR_WITHOUT_PRE[4], TEXT_WITHOUT_PRE, 6
244
245 assert_section_with_hash '', TEXT_WITHOUT_PRE, 0
246 assert_section_with_hash '', TEXT_WITHOUT_PRE, 10
247 end
248
249 def test_update_section_should_update_the_requested_section
250 replacement = "New text"
251
252 assert_equal [STR_WITHOUT_PRE[0], replacement, STR_WITHOUT_PRE[2..4]].flatten.join("\n\n"), @formatter.new(TEXT_WITHOUT_PRE).update_section(2, replacement)
253 assert_equal [STR_WITHOUT_PRE[0..1], replacement, STR_WITHOUT_PRE[4]].flatten.join("\n\n"), @formatter.new(TEXT_WITHOUT_PRE).update_section(3, replacement)
254 assert_equal [STR_WITHOUT_PRE[0..2], replacement, STR_WITHOUT_PRE[4]].flatten.join("\n\n"), @formatter.new(TEXT_WITHOUT_PRE).update_section(5, replacement)
255 assert_equal [STR_WITHOUT_PRE[0..3], replacement].flatten.join("\n\n"), @formatter.new(TEXT_WITHOUT_PRE).update_section(6, replacement)
256
257 assert_equal TEXT_WITHOUT_PRE, @formatter.new(TEXT_WITHOUT_PRE).update_section(0, replacement)
258 assert_equal TEXT_WITHOUT_PRE, @formatter.new(TEXT_WITHOUT_PRE).update_section(10, replacement)
259 end
260
261 def test_update_section_with_hash_should_update_the_requested_section
262 replacement = "New text"
263
264 assert_equal [STR_WITHOUT_PRE[0], replacement, STR_WITHOUT_PRE[2..4]].flatten.join("\n\n"),
265 @formatter.new(TEXT_WITHOUT_PRE).update_section(2, replacement, Digest::MD5.hexdigest(STR_WITHOUT_PRE[1]))
266 end
267
268 def test_update_section_with_wrong_hash_should_raise_an_error
269 assert_raise Redmine::WikiFormatting::StaleSectionError do
270 @formatter.new(TEXT_WITHOUT_PRE).update_section(2, "New text", Digest::MD5.hexdigest("Old text"))
271 end
272 end
273
274 STR_WITH_PRE = [
275 # 0
276 "h1. Title
277
278 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.",
279 # 1
280 "h2. Heading 2
281
282 Morbi facilisis accumsan orci non pharetra.
283
284 <pre>
285 Pre Content:
286
287 h2. Inside pre
288
289 Morbi facilisis accumsan orci non pharetra.
290 </pre>",
291 # 2
292 "h3. Heading 3
293
294 Nulla nunc nisi, egestas in ornare vel, posuere ac libero."]
295
296 def test_get_section_should_ignore_pre_content
297 text = STR_WITH_PRE.join("\n\n")
298
299 assert_section_with_hash STR_WITH_PRE[1..2].join("\n\n"), text, 2
300 assert_section_with_hash STR_WITH_PRE[2], text, 3
301 end
206
302
207 private
303 private
208
304
@@ -215,4 +311,13 EXPECTED
215 def to_html(text)
311 def to_html(text)
216 @formatter.new(text).to_html
312 @formatter.new(text).to_html
217 end
313 end
314
315 def assert_section_with_hash(expected, text, index)
316 result = @formatter.new(text).get_section(index)
317
318 assert_kind_of Array, result
319 assert_equal 2, result.size
320 assert_equal expected, result.first, "section content did not match"
321 assert_equal Digest::MD5.hexdigest(expected), result.last, "section hash did not match"
322 end
218 end
323 end
General Comments 0
You need to be logged in to leave comments. Login now