@@ -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) |
|
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 = $ |
|
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><%= |
|
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 = {} |
|
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"onclick=&#x61;&#x6c;&#x65;&#x72;&#x74;&#x28;&#x27;&#x58;&#x53;&#x53;&#x27;&#x29;;&#x22;" alt="" /></p>' |
|
204 | expected = '<p><img src="/images/comment.png"onclick=&#x61;&#x6c;&#x65;&#x72;&#x74;&#x28;&#x27;&#x58;&#x53;&#x53;&#x27;&#x29;;&#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