##// END OF EJS Templates
Adds export of all wiki pages to a PDF file (#3463)....
Jean-Philippe Lang -
r8614:3d27bf5318ef
parent child
Show More
@@ -1,317 +1,325
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'diff'
19 19
20 20 # The WikiController follows the Rails REST controller pattern but with
21 21 # a few differences
22 22 #
23 23 # * index - shows a list of WikiPages grouped by page or date
24 24 # * new - not used
25 25 # * create - not used
26 26 # * show - will also show the form for creating a new wiki page
27 27 # * edit - used to edit an existing or new page
28 28 # * update - used to save a wiki page update to the database, including new pages
29 29 # * destroy - normal
30 30 #
31 31 # Other member and collection methods are also used
32 32 #
33 33 # TODO: still being worked on
34 34 class WikiController < ApplicationController
35 35 default_search_scope :wiki_pages
36 36 before_filter :find_wiki, :authorize
37 37 before_filter :find_existing_or_new_page, :only => [:show, :edit, :update]
38 38 before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy]
39 39
40 40 helper :attachments
41 41 include AttachmentsHelper
42 42 helper :watchers
43 43 include Redmine::Export::PDF
44 44
45 45 # List of pages, sorted alphabetically and by parent (hierarchy)
46 46 def index
47 47 load_pages_for_index
48 48 @pages_by_parent_id = @pages.group_by(&:parent_id)
49 49 end
50 50
51 51 # List of page, by last update
52 52 def date_index
53 53 load_pages_for_index
54 54 @pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
55 55 end
56 56
57 57 # display a page (in editing mode if it doesn't exist)
58 58 def show
59 59 if @page.new_record?
60 60 if User.current.allowed_to?(:edit_wiki_pages, @project) && editable?
61 61 edit
62 62 render :action => 'edit'
63 63 else
64 64 render_404
65 65 end
66 66 return
67 67 end
68 68 if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project)
69 69 # Redirects user to the current version if he's not allowed to view previous versions
70 70 redirect_to :version => nil
71 71 return
72 72 end
73 73 @content = @page.content_for_version(params[:version])
74 74 if User.current.allowed_to?(:export_wiki_pages, @project)
75 75 if params[:format] == 'pdf'
76 send_data(wiki_to_pdf(@page, @project), :type => 'application/pdf', :filename => "#{@page.title}.pdf")
76 send_data(wiki_page_to_pdf(@page, @project), :type => 'application/pdf', :filename => "#{@page.title}.pdf")
77 77 return
78 78 elsif params[:format] == 'html'
79 79 export = render_to_string :action => 'export', :layout => false
80 80 send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
81 81 return
82 82 elsif params[:format] == 'txt'
83 83 send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
84 84 return
85 85 end
86 86 end
87 87 @editable = editable?
88 88 @sections_editable = @editable && User.current.allowed_to?(:edit_wiki_pages, @page.project) &&
89 89 @content.current_version? &&
90 90 Redmine::WikiFormatting.supports_section_edit?
91 91
92 92 render :action => 'show'
93 93 end
94 94
95 95 # edit an existing page or a new one
96 96 def edit
97 97 return render_403 unless editable?
98 98 if @page.new_record?
99 99 @page.content = WikiContent.new(:page => @page)
100 100 if params[:parent].present?
101 101 @page.parent = @page.wiki.find_page(params[:parent].to_s)
102 102 end
103 103 end
104 104
105 105 @content = @page.content_for_version(params[:version])
106 106 @content.text = initial_page_content(@page) if @content.text.blank?
107 107 # don't keep previous comment
108 108 @content.comments = nil
109 109
110 110 # To prevent StaleObjectError exception when reverting to a previous version
111 111 @content.version = @page.content.version
112 112
113 113 @text = @content.text
114 114 if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
115 115 @section = params[:section].to_i
116 116 @text, @section_hash = Redmine::WikiFormatting.formatter.new(@text).get_section(@section)
117 117 render_404 if @text.blank?
118 118 end
119 119 end
120 120
121 121 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
122 122 # Creates a new page or updates an existing one
123 123 def update
124 124 return render_403 unless editable?
125 125 @page.content = WikiContent.new(:page => @page) if @page.new_record?
126 126
127 127 @content = @page.content_for_version(params[:version])
128 128 @content.text = initial_page_content(@page) if @content.text.blank?
129 129 # don't keep previous comment
130 130 @content.comments = nil
131 131
132 132 if !@page.new_record? && params[:content].present? && @content.text == params[:content][:text]
133 133 attachments = Attachment.attach_files(@page, params[:attachments])
134 134 render_attachment_warning_if_needed(@page)
135 135 # don't save if text wasn't changed
136 136 redirect_to :action => 'show', :project_id => @project, :id => @page.title
137 137 return
138 138 end
139 139
140 140 @content.comments = params[:content][:comments]
141 141 @text = params[:content][:text]
142 142 if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
143 143 @section = params[:section].to_i
144 144 @section_hash = params[:section_hash]
145 145 @content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(params[:section].to_i, @text, @section_hash)
146 146 else
147 147 @content.version = params[:content][:version]
148 148 @content.text = @text
149 149 end
150 150 @content.author = User.current
151 151 if @page.new_record? && params[:page]
152 152 @page.parent_id = params[:page][:parent_id]
153 153 end
154 154 # if page is new @page.save will also save content, but not if page isn't a new record
155 155 if (@page.new_record? ? @page.save : @content.save)
156 156 attachments = Attachment.attach_files(@page, params[:attachments])
157 157 render_attachment_warning_if_needed(@page)
158 158 call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
159 159 redirect_to :action => 'show', :project_id => @project, :id => @page.title
160 160 else
161 161 render :action => 'edit'
162 162 end
163 163
164 164 rescue ActiveRecord::StaleObjectError, Redmine::WikiFormatting::StaleSectionError
165 165 # Optimistic locking exception
166 166 flash.now[:error] = l(:notice_locking_conflict)
167 167 render :action => 'edit'
168 168 end
169 169
170 170 # rename a page
171 171 def rename
172 172 return render_403 unless editable?
173 173 @page.redirect_existing_links = true
174 174 # used to display the *original* title if some AR validation errors occur
175 175 @original_title = @page.pretty_title
176 176 if request.post? && @page.update_attributes(params[:wiki_page])
177 177 flash[:notice] = l(:notice_successful_update)
178 178 redirect_to :action => 'show', :project_id => @project, :id => @page.title
179 179 end
180 180 end
181 181
182 182 verify :method => :post, :only => :protect, :redirect_to => { :action => :show }
183 183 def protect
184 184 @page.update_attribute :protected, params[:protected]
185 185 redirect_to :action => 'show', :project_id => @project, :id => @page.title
186 186 end
187 187
188 188 # show page history
189 189 def history
190 190 @version_count = @page.content.versions.count
191 191 @version_pages = Paginator.new self, @version_count, per_page_option, params['p']
192 192 # don't load text
193 193 @versions = @page.content.versions.find :all,
194 194 :select => "id, author_id, comments, updated_on, version",
195 195 :order => 'version DESC',
196 196 :limit => @version_pages.items_per_page + 1,
197 197 :offset => @version_pages.current.offset
198 198
199 199 render :layout => false if request.xhr?
200 200 end
201 201
202 202 def diff
203 203 @diff = @page.diff(params[:version], params[:version_from])
204 204 render_404 unless @diff
205 205 end
206 206
207 207 def annotate
208 208 @annotate = @page.annotate(params[:version])
209 209 render_404 unless @annotate
210 210 end
211 211
212 212 verify :method => :delete, :only => [:destroy], :redirect_to => { :action => :show }
213 213 # Removes a wiki page and its history
214 214 # Children can be either set as root pages, removed or reassigned to another parent page
215 215 def destroy
216 216 return render_403 unless editable?
217 217
218 218 @descendants_count = @page.descendants.size
219 219 if @descendants_count > 0
220 220 case params[:todo]
221 221 when 'nullify'
222 222 # Nothing to do
223 223 when 'destroy'
224 224 # Removes all its descendants
225 225 @page.descendants.each(&:destroy)
226 226 when 'reassign'
227 227 # Reassign children to another parent page
228 228 reassign_to = @wiki.pages.find_by_id(params[:reassign_to_id].to_i)
229 229 return unless reassign_to
230 230 @page.children.each do |child|
231 231 child.update_attribute(:parent, reassign_to)
232 232 end
233 233 else
234 234 @reassignable_to = @wiki.pages - @page.self_and_descendants
235 235 return
236 236 end
237 237 end
238 238 @page.destroy
239 239 redirect_to :action => 'index', :project_id => @project
240 240 end
241 241
242 # Export wiki to a single html file
242 # Export wiki to a single pdf or html file
243 243 def export
244 if User.current.allowed_to?(:export_wiki_pages, @project)
245 @pages = @wiki.pages.find :all, :order => 'title'
246 export = render_to_string :action => 'export_multiple', :layout => false
247 send_data(export, :type => 'text/html', :filename => "wiki.html")
248 else
244 unless User.current.allowed_to?(:export_wiki_pages, @project)
249 245 redirect_to :action => 'show', :project_id => @project, :id => nil
246 return
247 end
248
249 @pages = @wiki.pages.all(:order => 'title', :include => [:content, :attachments], :limit => 75)
250 respond_to do |format|
251 format.html {
252 export = render_to_string :action => 'export_multiple', :layout => false
253 send_data(export, :type => 'text/html', :filename => "wiki.html")
254 }
255 format.pdf {
256 send_data(wiki_pages_to_pdf(@pages, @project), :type => 'application/pdf', :filename => "#{@project.identifier}.pdf")
257 }
250 258 end
251 259 end
252 260
253 261 def preview
254 262 page = @wiki.find_page(params[:id])
255 263 # page is nil when previewing a new page
256 264 return render_403 unless page.nil? || editable?(page)
257 265 if page
258 266 @attachements = page.attachments
259 267 @previewed = page.content
260 268 end
261 269 @text = params[:content][:text]
262 270 render :partial => 'common/preview'
263 271 end
264 272
265 273 def add_attachment
266 274 return render_403 unless editable?
267 275 attachments = Attachment.attach_files(@page, params[:attachments])
268 276 render_attachment_warning_if_needed(@page)
269 277 redirect_to :action => 'show', :id => @page.title, :project_id => @project
270 278 end
271 279
272 280 private
273 281
274 282 def find_wiki
275 283 @project = Project.find(params[:project_id])
276 284 @wiki = @project.wiki
277 285 render_404 unless @wiki
278 286 rescue ActiveRecord::RecordNotFound
279 287 render_404
280 288 end
281 289
282 290 # Finds the requested page or a new page if it doesn't exist
283 291 def find_existing_or_new_page
284 292 @page = @wiki.find_or_new_page(params[:id])
285 293 if @wiki.page_found_with_redirect?
286 294 redirect_to params.update(:id => @page.title)
287 295 end
288 296 end
289 297
290 298 # Finds the requested page and returns a 404 error if it doesn't exist
291 299 def find_existing_page
292 300 @page = @wiki.find_page(params[:id])
293 301 if @page.nil?
294 302 render_404
295 303 return
296 304 end
297 305 if @wiki.page_found_with_redirect?
298 306 redirect_to params.update(:id => @page.title)
299 307 end
300 308 end
301 309
302 310 # Returns true if the current user is allowed to edit the page, otherwise false
303 311 def editable?(page = @page)
304 312 page.editable_by?(User.current)
305 313 end
306 314
307 315 # Returns the default content of a new wiki page
308 316 def initial_page_content(page)
309 317 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
310 318 extend helper unless self.instance_of?(helper)
311 319 helper.instance_method(:initial_page_content).bind(self).call(page)
312 320 end
313 321
314 322 def load_pages_for_index
315 323 @pages = @wiki.pages.with_updated_on.all(:order => 'title', :include => {:wiki => :project})
316 324 end
317 325 end
@@ -1,33 +1,36
1 1 <div class="contextual">
2 2 <%= watcher_tag(@wiki, User.current) %>
3 3 </div>
4 4
5 5 <h2><%= l(:label_index_by_date) %></h2>
6 6
7 7 <% if @pages.empty? %>
8 8 <p class="nodata"><%= l(:label_no_data) %></p>
9 9 <% end %>
10 10
11 11 <% @pages_by_date.keys.sort.reverse.each do |date| %>
12 12 <h3><%= format_date(date) %></h3>
13 13 <ul>
14 14 <% @pages_by_date[date].each do |page| %>
15 15 <li><%= link_to h(page.pretty_title), :action => 'show', :id => page.title, :project_id => page.project %></li>
16 16 <% end %>
17 17 </ul>
18 18 <% end %>
19 19
20 20 <% content_for :sidebar do %>
21 21 <%= render :partial => 'sidebar' %>
22 22 <% end %>
23 23
24 24 <% unless @pages.empty? %>
25 25 <% other_formats_links do |f| %>
26 26 <%= f.link_to 'Atom', :url => {:controller => 'activities', :action => 'index', :id => @project, :show_wiki_edits => 1, :key => User.current.rss_key} %>
27 <%= f.link_to('HTML', :url => {:action => 'export'}) if User.current.allowed_to?(:export_wiki_pages, @project) %>
27 <% if User.current.allowed_to?(:export_wiki_pages, @project) %>
28 <%= f.link_to('PDF', :url => {:action => 'export', :format => 'pdf'}) %>
29 <%= f.link_to('HTML', :url => {:action => 'export'}) %>
30 <% end %>
28 31 <% end %>
29 32 <% end %>
30 33
31 34 <% content_for :header_tags do %>
32 35 <%= auto_discovery_link_tag(:atom, :controller => 'activities', :action => 'index', :id => @project, :show_wiki_edits => 1, :format => 'atom', :key => User.current.rss_key) %>
33 36 <% end %>
@@ -1,34 +1,35
1 1 <div class="contextual">
2 2 <%= watcher_tag(@wiki, User.current) %>
3 3 </div>
4 4
5 5 <h2><%= l(:label_index_by_title) %></h2>
6 6
7 7 <% if @pages.empty? %>
8 8 <p class="nodata"><%= l(:label_no_data) %></p>
9 9 <% end %>
10 10
11 11 <%= render_page_hierarchy(@pages_by_parent_id, nil, :timestamp => true) %>
12 12
13 13 <% content_for :sidebar do %>
14 14 <%= render :partial => 'sidebar' %>
15 15 <% end %>
16 16
17 17 <% unless @pages.empty? %>
18 18 <% other_formats_links do |f| %>
19 19 <%= f.link_to 'Atom',
20 20 :url => {:controller => 'activities', :action => 'index',
21 21 :id => @project, :show_wiki_edits => 1,
22 22 :key => User.current.rss_key} %>
23 <%= f.link_to('HTML',
24 :url => {:action => 'export'}
25 ) if User.current.allowed_to?(:export_wiki_pages, @project) %>
23 <% if User.current.allowed_to?(:export_wiki_pages, @project) %>
24 <%= f.link_to('PDF', :url => {:action => 'export', :format => 'pdf'}) %>
25 <%= f.link_to('HTML', :url => {:action => 'export'}) %>
26 <% end %>
26 27 <% end %>
27 28 <% end %>
28 29
29 30 <% content_for :header_tags do %>
30 31 <%= auto_discovery_link_tag(
31 32 :atom, :controller => 'activities', :action => 'index',
32 33 :id => @project, :show_wiki_edits => 1, :format => 'atom',
33 34 :key => User.current.rss_key) %>
34 35 <% end %>
@@ -1,634 +1,670
1 1 # encoding: utf-8
2 2 #
3 3 # Redmine - project management software
4 4 # Copyright (C) 2006-2011 Jean-Philippe Lang
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; either version 2
9 9 # of the License, or (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 require 'iconv'
21 21 require 'fpdf/chinese'
22 22 require 'fpdf/japanese'
23 23 require 'fpdf/korean'
24 24 require 'core/rmagick'
25 25
26 26 module Redmine
27 27 module Export
28 28 module PDF
29 29 include ActionView::Helpers::TextHelper
30 30 include ActionView::Helpers::NumberHelper
31 31 include IssuesHelper
32 32
33 33 class ITCPDF < TCPDF
34 34 include Redmine::I18n
35 35 attr_accessor :footer_date
36 36
37 37 def initialize(lang)
38 38 @@k_path_cache = Rails.root.join('tmp', 'pdf')
39 39 FileUtils.mkdir_p @@k_path_cache unless File::exist?(@@k_path_cache)
40 40 set_language_if_valid lang
41 41 pdf_encoding = l(:general_pdf_encoding).upcase
42 42 super('P', 'mm', 'A4', (pdf_encoding == 'UTF-8'), pdf_encoding)
43 43 case current_language.to_s.downcase
44 44 when 'vi'
45 45 @font_for_content = 'DejaVuSans'
46 46 @font_for_footer = 'DejaVuSans'
47 47 else
48 48 case pdf_encoding
49 49 when 'UTF-8'
50 50 @font_for_content = 'FreeSans'
51 51 @font_for_footer = 'FreeSans'
52 52 when 'CP949'
53 53 extend(PDF_Korean)
54 54 AddUHCFont()
55 55 @font_for_content = 'UHC'
56 56 @font_for_footer = 'UHC'
57 57 when 'CP932', 'SJIS', 'SHIFT_JIS'
58 58 extend(PDF_Japanese)
59 59 AddSJISFont()
60 60 @font_for_content = 'SJIS'
61 61 @font_for_footer = 'SJIS'
62 62 when 'GB18030'
63 63 extend(PDF_Chinese)
64 64 AddGBFont()
65 65 @font_for_content = 'GB'
66 66 @font_for_footer = 'GB'
67 67 when 'BIG5'
68 68 extend(PDF_Chinese)
69 69 AddBig5Font()
70 70 @font_for_content = 'Big5'
71 71 @font_for_footer = 'Big5'
72 72 else
73 73 @font_for_content = 'Arial'
74 74 @font_for_footer = 'Helvetica'
75 75 end
76 76 end
77 77 SetCreator(Redmine::Info.app_name)
78 78 SetFont(@font_for_content)
79 79 @outlines = []
80 80 @outlineRoot = nil
81 81 end
82 82
83 83 def SetFontStyle(style, size)
84 84 SetFont(@font_for_content, style, size)
85 85 end
86 86
87 87 def SetTitle(txt)
88 88 txt = begin
89 89 utf16txt = Iconv.conv('UTF-16BE', 'UTF-8', txt)
90 90 hextxt = "<FEFF" # FEFF is BOM
91 91 hextxt << utf16txt.unpack("C*").map {|x| sprintf("%02X",x) }.join
92 92 hextxt << ">"
93 93 rescue
94 94 txt
95 95 end || ''
96 96 super(txt)
97 97 end
98 98
99 99 def textstring(s)
100 100 # Format a text string
101 101 if s =~ /^</ # This means the string is hex-dumped.
102 102 return s
103 103 else
104 104 return '('+escape(s)+')'
105 105 end
106 106 end
107 107
108 108 def fix_text_encoding(txt)
109 109 RDMPdfEncoding::rdm_from_utf8(txt, l(:general_pdf_encoding))
110 110 end
111 111
112 112 def RDMCell(w ,h=0, txt='', border=0, ln=0, align='', fill=0, link='')
113 113 Cell(w, h, fix_text_encoding(txt), border, ln, align, fill, link)
114 114 end
115 115
116 116 def RDMMultiCell(w, h=0, txt='', border=0, align='', fill=0, ln=1)
117 117 MultiCell(w, h, fix_text_encoding(txt), border, align, fill, ln)
118 118 end
119 119
120 120 def RDMwriteHTMLCell(w, h, x, y, txt='', attachments=[], border=0, ln=1, fill=0)
121 121 @attachments = attachments
122 122 writeHTMLCell(w, h, x, y,
123 123 fix_text_encoding(
124 124 Redmine::WikiFormatting.to_html(Setting.text_formatting, txt)),
125 125 border, ln, fill)
126 126 end
127 127
128 128 def getImageFilename(attrname)
129 129 # attrname: general_pdf_encoding string file/uri name
130 130 atta = RDMPdfEncoding.attach(@attachments, attrname, l(:general_pdf_encoding))
131 131 if atta
132 132 return atta.diskfile
133 133 else
134 134 return nil
135 135 end
136 136 end
137 137
138 138 def Footer
139 139 SetFont(@font_for_footer, 'I', 8)
140 140 SetY(-15)
141 141 SetX(15)
142 142 RDMCell(0, 5, @footer_date, 0, 0, 'L')
143 143 SetY(-15)
144 144 SetX(-30)
145 145 RDMCell(0, 5, PageNo().to_s + '/{nb}', 0, 0, 'C')
146 146 end
147 147
148 148 def Bookmark(txt, level=0, y=0)
149 149 utf16 = Iconv.conv('UTF-16', 'UTF-8', txt)
150 150 if (y == -1)
151 151 y = GetY()
152 152 end
153 153 @outlines << {:t => utf16, :l => level, :p => PageNo(), :y => (@h - y)*@k}
154 154 end
155 155
156 156 def putbookmarks
157 157 nb=@outlines.size
158 158 return if (nb==0)
159 159 lru=[]
160 160 level=0
161 161 @outlines.each_with_index do |o, i|
162 162 if(o[:l]>0)
163 163 parent=lru[o[:l]-1]
164 164 #Set parent and last pointers
165 165 @outlines[i][:parent]=parent
166 166 @outlines[parent][:last]=i
167 167 if (o[:l]>level)
168 168 #Level increasing: set first pointer
169 169 @outlines[parent][:first]=i
170 170 end
171 171 else
172 172 @outlines[i][:parent]=nb
173 173 end
174 174 if (o[:l]<=level && i>0)
175 175 #Set prev and next pointers
176 176 prev=lru[o[:l]]
177 177 @outlines[prev][:next]=i
178 178 @outlines[i][:prev]=prev
179 179 end
180 180 lru[o[:l]]=i
181 181 level=o[:l]
182 182 end
183 183 #Outline items
184 184 n=self.n+1
185 185 @outlines.each_with_index do |o, i|
186 186 newobj()
187 187 out('<</Title '+textstring(o[:t]))
188 188 out("/Parent #{n+o[:parent]} 0 R")
189 189 if (o[:prev])
190 190 out("/Prev #{n+o[:prev]} 0 R")
191 191 end
192 192 if (o[:next])
193 193 out("/Next #{n+o[:next]} 0 R")
194 194 end
195 195 if (o[:first])
196 196 out("/First #{n+o[:first]} 0 R")
197 197 end
198 198 if (o[:last])
199 199 out("/Last #{n+o[:last]} 0 R")
200 200 end
201 201 out("/Dest [%d 0 R /XYZ 0 %.2f null]" % [1+2*o[:p], o[:y]])
202 202 out('/Count 0>>')
203 203 out('endobj')
204 204 end
205 205 #Outline root
206 206 newobj()
207 207 @outlineRoot=self.n
208 208 out("<</Type /Outlines /First #{n} 0 R");
209 209 out("/Last #{n+lru[0]} 0 R>>");
210 210 out('endobj');
211 211 end
212 212
213 213 def putresources()
214 214 super
215 215 putbookmarks()
216 216 end
217 217
218 218 def putcatalog()
219 219 super
220 220 if(@outlines.size > 0)
221 221 out("/Outlines #{@outlineRoot} 0 R");
222 222 out('/PageMode /UseOutlines');
223 223 end
224 224 end
225 225 end
226 226
227 227 # Returns a PDF string of a list of issues
228 228 def issues_to_pdf(issues, project, query)
229 229 pdf = ITCPDF.new(current_language)
230 230 title = query.new_record? ? l(:label_issue_plural) : query.name
231 231 title = "#{project} - #{title}" if project
232 232 pdf.SetTitle(title)
233 233 pdf.alias_nb_pages
234 234 pdf.footer_date = format_date(Date.today)
235 235 pdf.SetAutoPageBreak(false)
236 236 pdf.AddPage("L")
237 237
238 238 # Landscape A4 = 210 x 297 mm
239 239 page_height = 210
240 240 page_width = 297
241 241 right_margin = 10
242 242 bottom_margin = 20
243 243 col_id_width = 10
244 244 row_height = 5
245 245
246 246 # column widths
247 247 table_width = page_width - right_margin - 10 # fixed left margin
248 248 col_width = []
249 249 unless query.columns.empty?
250 250 col_width = query.columns.collect do |c|
251 251 (c.name == :subject || (c.is_a?(QueryCustomFieldColumn) &&
252 252 ['string', 'text'].include?(c.custom_field.field_format))) ? 4.0 : 1.0
253 253 end
254 254 ratio = (table_width - col_id_width) / col_width.inject(0) {|s,w| s += w}
255 255 col_width = col_width.collect {|w| w * ratio}
256 256 end
257 257
258 258 # title
259 259 pdf.SetFontStyle('B',11)
260 260 pdf.RDMCell(190,10, title)
261 261 pdf.Ln
262 262
263 263 # headers
264 264 pdf.SetFontStyle('B',8)
265 265 pdf.SetFillColor(230, 230, 230)
266 266
267 267 # render it background to find the max height used
268 268 base_x = pdf.GetX
269 269 base_y = pdf.GetY
270 270 max_height = issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
271 271 pdf.Rect(base_x, base_y, table_width, max_height, 'FD');
272 272 pdf.SetXY(base_x, base_y);
273 273
274 274 # write the cells on page
275 275 pdf.RDMCell(col_id_width, row_height, "#", "T", 0, 'C', 1)
276 276 issues_to_pdf_write_cells(pdf, query.columns, col_width, row_height, true)
277 277 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
278 278 pdf.SetY(base_y + max_height);
279 279
280 280 # rows
281 281 pdf.SetFontStyle('',8)
282 282 pdf.SetFillColor(255, 255, 255)
283 283 previous_group = false
284 284 issue_list(issues) do |issue, level|
285 285 if query.grouped? &&
286 286 (group = query.group_by_column.value(issue)) != previous_group
287 287 pdf.SetFontStyle('B',9)
288 288 group_label = group.blank? ? 'None' : group.to_s
289 289 group_label << " (#{query.issue_count_by_group[group]})"
290 290 pdf.Bookmark group_label, 0, -1
291 291 pdf.RDMCell(277, row_height, group_label, 1, 1, 'L')
292 292 pdf.SetFontStyle('',8)
293 293 previous_group = group
294 294 end
295 295 # fetch all the row values
296 296 col_values = query.columns.collect do |column|
297 297 s = if column.is_a?(QueryCustomFieldColumn)
298 298 cv = issue.custom_field_values.detect {|v| v.custom_field_id == column.custom_field.id}
299 299 show_value(cv)
300 300 else
301 301 value = issue.send(column.name)
302 302 if column.name == :subject
303 303 value = " " * level + value
304 304 end
305 305 if value.is_a?(Date)
306 306 format_date(value)
307 307 elsif value.is_a?(Time)
308 308 format_time(value)
309 309 else
310 310 value
311 311 end
312 312 end
313 313 s.to_s
314 314 end
315 315
316 316 # render it off-page to find the max height used
317 317 base_x = pdf.GetX
318 318 base_y = pdf.GetY
319 319 pdf.SetY(2 * page_height)
320 320 max_height = issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
321 321 pdf.SetXY(base_x, base_y)
322 322
323 323 # make new page if it doesn't fit on the current one
324 324 space_left = page_height - base_y - bottom_margin
325 325 if max_height > space_left
326 326 pdf.AddPage("L")
327 327 base_x = pdf.GetX
328 328 base_y = pdf.GetY
329 329 end
330 330
331 331 # write the cells on page
332 332 pdf.RDMCell(col_id_width, row_height, issue.id.to_s, "T", 0, 'C', 1)
333 333 issues_to_pdf_write_cells(pdf, col_values, col_width, row_height)
334 334 issues_to_pdf_draw_borders(pdf, base_x, base_y, base_y + max_height, col_id_width, col_width)
335 335 pdf.SetY(base_y + max_height);
336 336 end
337 337
338 338 if issues.size == Setting.issues_export_limit.to_i
339 339 pdf.SetFontStyle('B',10)
340 340 pdf.RDMCell(0, row_height, '...')
341 341 end
342 342 pdf.Output
343 343 end
344 344
345 345 # Renders MultiCells and returns the maximum height used
346 346 def issues_to_pdf_write_cells(pdf, col_values, col_widths,
347 347 row_height, head=false)
348 348 base_y = pdf.GetY
349 349 max_height = row_height
350 350 col_values.each_with_index do |column, i|
351 351 col_x = pdf.GetX
352 352 if head == true
353 353 pdf.RDMMultiCell(col_widths[i], row_height, column.caption, "T", 'L', 1)
354 354 else
355 355 pdf.RDMMultiCell(col_widths[i], row_height, column, "T", 'L', 1)
356 356 end
357 357 max_height = (pdf.GetY - base_y) if (pdf.GetY - base_y) > max_height
358 358 pdf.SetXY(col_x + col_widths[i], base_y);
359 359 end
360 360 return max_height
361 361 end
362 362
363 363 # Draw lines to close the row (MultiCell border drawing in not uniform)
364 364 def issues_to_pdf_draw_borders(pdf, top_x, top_y, lower_y,
365 365 id_width, col_widths)
366 366 col_x = top_x + id_width
367 367 pdf.Line(col_x, top_y, col_x, lower_y) # id right border
368 368 col_widths.each do |width|
369 369 col_x += width
370 370 pdf.Line(col_x, top_y, col_x, lower_y) # columns right border
371 371 end
372 372 pdf.Line(top_x, top_y, top_x, lower_y) # left border
373 373 pdf.Line(top_x, lower_y, col_x, lower_y) # bottom border
374 374 end
375 375
376 376 # Returns a PDF string of a single issue
377 377 def issue_to_pdf(issue)
378 378 pdf = ITCPDF.new(current_language)
379 379 pdf.SetTitle("#{issue.project} - ##{issue.tracker} #{issue.id}")
380 380 pdf.alias_nb_pages
381 381 pdf.footer_date = format_date(Date.today)
382 382 pdf.AddPage
383 383 pdf.SetFontStyle('B',11)
384 384 buf = "#{issue.project} - #{issue.tracker} # #{issue.id}"
385 385 pdf.RDMMultiCell(190, 5, buf)
386 386 pdf.Ln
387 387 pdf.SetFontStyle('',8)
388 388 base_x = pdf.GetX
389 389 i = 1
390 390 issue.ancestors.each do |ancestor|
391 391 pdf.SetX(base_x + i)
392 392 buf = "#{ancestor.tracker} # #{ancestor.id} (#{ancestor.status.to_s}): #{ancestor.subject}"
393 393 pdf.RDMMultiCell(190 - i, 5, buf)
394 394 i += 1 if i < 35
395 395 end
396 396 pdf.Ln
397 397
398 398 pdf.SetFontStyle('B',9)
399 399 pdf.RDMCell(35,5, l(:field_status) + ":","LT")
400 400 pdf.SetFontStyle('',9)
401 401 pdf.RDMCell(60,5, issue.status.to_s,"RT")
402 402 pdf.SetFontStyle('B',9)
403 403 pdf.RDMCell(35,5, l(:field_priority) + ":","LT")
404 404 pdf.SetFontStyle('',9)
405 405 pdf.RDMCell(60,5, issue.priority.to_s,"RT")
406 406 pdf.Ln
407 407
408 408 pdf.SetFontStyle('B',9)
409 409 pdf.RDMCell(35,5, l(:field_author) + ":","L")
410 410 pdf.SetFontStyle('',9)
411 411 pdf.RDMCell(60,5, issue.author.to_s,"R")
412 412 pdf.SetFontStyle('B',9)
413 413 pdf.RDMCell(35,5, l(:field_category) + ":","L")
414 414 pdf.SetFontStyle('',9)
415 415 pdf.RDMCell(60,5, issue.category.to_s,"R")
416 416 pdf.Ln
417 417
418 418 pdf.SetFontStyle('B',9)
419 419 pdf.RDMCell(35,5, l(:field_created_on) + ":","L")
420 420 pdf.SetFontStyle('',9)
421 421 pdf.RDMCell(60,5, format_date(issue.created_on),"R")
422 422 pdf.SetFontStyle('B',9)
423 423 pdf.RDMCell(35,5, l(:field_assigned_to) + ":","L")
424 424 pdf.SetFontStyle('',9)
425 425 pdf.RDMCell(60,5, issue.assigned_to.to_s,"R")
426 426 pdf.Ln
427 427
428 428 pdf.SetFontStyle('B',9)
429 429 pdf.RDMCell(35,5, l(:field_updated_on) + ":","LB")
430 430 pdf.SetFontStyle('',9)
431 431 pdf.RDMCell(60,5, format_date(issue.updated_on),"RB")
432 432 pdf.SetFontStyle('B',9)
433 433 pdf.RDMCell(35,5, l(:field_due_date) + ":","LB")
434 434 pdf.SetFontStyle('',9)
435 435 pdf.RDMCell(60,5, format_date(issue.due_date),"RB")
436 436 pdf.Ln
437 437
438 438 for custom_value in issue.custom_field_values
439 439 pdf.SetFontStyle('B',9)
440 440 pdf.RDMCell(35,5, custom_value.custom_field.name + ":","L")
441 441 pdf.SetFontStyle('',9)
442 442 pdf.RDMMultiCell(155,5, (show_value custom_value),"R")
443 443 end
444 444
445 445 y0 = pdf.GetY
446 446
447 447 pdf.SetFontStyle('B',9)
448 448 pdf.RDMCell(35,5, l(:field_subject) + ":","LT")
449 449 pdf.SetFontStyle('',9)
450 450 pdf.RDMMultiCell(155,5, issue.subject,"RT")
451 451 pdf.Line(pdf.GetX, y0, pdf.GetX, pdf.GetY)
452 452
453 453 pdf.SetFontStyle('B',9)
454 454 pdf.RDMCell(35+155, 5, l(:field_description), "LRT", 1)
455 455 pdf.SetFontStyle('',9)
456 456
457 457 # Set resize image scale
458 458 pdf.SetImageScale(1.6)
459 459 pdf.RDMwriteHTMLCell(35+155, 5, 0, 0,
460 460 issue.description.to_s, issue.attachments, "LRB")
461 461
462 462 unless issue.leaf?
463 463 # for CJK
464 464 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 90 : 65 )
465 465
466 466 pdf.SetFontStyle('B',9)
467 467 pdf.RDMCell(35+155,5, l(:label_subtask_plural) + ":", "LTR")
468 468 pdf.Ln
469 469 issue_list(issue.descendants.sort_by(&:lft)) do |child, level|
470 470 buf = truncate("#{child.tracker} # #{child.id}: #{child.subject}",
471 471 :length => truncate_length)
472 472 level = 10 if level >= 10
473 473 pdf.SetFontStyle('',8)
474 474 pdf.RDMCell(35+135,5, (level >=1 ? " " * level : "") + buf, "L")
475 475 pdf.SetFontStyle('B',8)
476 476 pdf.RDMCell(20,5, child.status.to_s, "R")
477 477 pdf.Ln
478 478 end
479 479 end
480 480
481 481 relations = issue.relations.select { |r| r.other_issue(issue).visible? }
482 482 unless relations.empty?
483 483 # for CJK
484 484 truncate_length = ( l(:general_pdf_encoding).upcase == "UTF-8" ? 80 : 60 )
485 485
486 486 pdf.SetFontStyle('B',9)
487 487 pdf.RDMCell(35+155,5, l(:label_related_issues) + ":", "LTR")
488 488 pdf.Ln
489 489 relations.each do |relation|
490 490 buf = ""
491 491 buf += "#{l(relation.label_for(issue))} "
492 492 if relation.delay && relation.delay != 0
493 493 buf += "(#{l('datetime.distance_in_words.x_days', :count => relation.delay)}) "
494 494 end
495 495 if Setting.cross_project_issue_relations?
496 496 buf += "#{relation.other_issue(issue).project} - "
497 497 end
498 498 buf += "#{relation.other_issue(issue).tracker}" +
499 499 " # #{relation.other_issue(issue).id}: #{relation.other_issue(issue).subject}"
500 500 buf = truncate(buf, :length => truncate_length)
501 501 pdf.SetFontStyle('', 8)
502 502 pdf.RDMCell(35+155-60, 5, buf, "L")
503 503 pdf.SetFontStyle('B',8)
504 504 pdf.RDMCell(20,5, relation.other_issue(issue).status.to_s, "")
505 505 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).start_date), "")
506 506 pdf.RDMCell(20,5, format_date(relation.other_issue(issue).due_date), "R")
507 507 pdf.Ln
508 508 end
509 509 end
510 510 pdf.RDMCell(190,5, "", "T")
511 511 pdf.Ln
512 512
513 513 if issue.changesets.any? &&
514 514 User.current.allowed_to?(:view_changesets, issue.project)
515 515 pdf.SetFontStyle('B',9)
516 516 pdf.RDMCell(190,5, l(:label_associated_revisions), "B")
517 517 pdf.Ln
518 518 for changeset in issue.changesets
519 519 pdf.SetFontStyle('B',8)
520 520 csstr = "#{l(:label_revision)} #{changeset.format_identifier} - "
521 521 csstr += format_time(changeset.committed_on) + " - " + changeset.author.to_s
522 522 pdf.RDMCell(190, 5, csstr)
523 523 pdf.Ln
524 524 unless changeset.comments.blank?
525 525 pdf.SetFontStyle('',8)
526 526 pdf.RDMwriteHTMLCell(190,5,0,0,
527 527 changeset.comments.to_s, issue.attachments, "")
528 528 end
529 529 pdf.Ln
530 530 end
531 531 end
532 532
533 533 pdf.SetFontStyle('B',9)
534 534 pdf.RDMCell(190,5, l(:label_history), "B")
535 535 pdf.Ln
536 536 indice = 0
537 537 for journal in issue.journals.find(
538 538 :all, :include => [:user, :details],
539 539 :order => "#{Journal.table_name}.created_on ASC")
540 540 indice = indice + 1
541 541 pdf.SetFontStyle('B',8)
542 542 pdf.RDMCell(190,5,
543 543 "#" + indice.to_s +
544 544 " - " + format_time(journal.created_on) +
545 545 " - " + journal.user.name)
546 546 pdf.Ln
547 547 pdf.SetFontStyle('I',8)
548 548 details_to_strings(journal.details, true).each do |string|
549 549 pdf.RDMMultiCell(190,5, "- " + string)
550 550 end
551 551 if journal.notes?
552 552 pdf.Ln unless journal.details.empty?
553 553 pdf.SetFontStyle('',8)
554 554 pdf.RDMwriteHTMLCell(190,5,0,0,
555 555 journal.notes.to_s, issue.attachments, "")
556 556 end
557 557 pdf.Ln
558 558 end
559 559
560 560 if issue.attachments.any?
561 561 pdf.SetFontStyle('B',9)
562 562 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
563 563 pdf.Ln
564 564 for attachment in issue.attachments
565 565 pdf.SetFontStyle('',8)
566 566 pdf.RDMCell(80,5, attachment.filename)
567 567 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
568 568 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
569 569 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
570 570 pdf.Ln
571 571 end
572 572 end
573 573 pdf.Output
574 574 end
575 575
576 # Returns a PDF string of a set of wiki pages
577 def wiki_pages_to_pdf(pages, project)
578 pdf = ITCPDF.new(current_language)
579 pdf.SetTitle(project.name)
580 pdf.alias_nb_pages
581 pdf.footer_date = format_date(Date.today)
582 pdf.AddPage
583 pdf.SetFontStyle('B',11)
584 pdf.RDMMultiCell(190,5, project.name)
585 pdf.Ln
586 # Set resize image scale
587 pdf.SetImageScale(1.6)
588 pdf.SetFontStyle('',9)
589 write_page_hierarchy(pdf, pages.group_by(&:parent_id))
590 pdf.Output
591 end
592
576 593 # Returns a PDF string of a single wiki page
577 def wiki_to_pdf(page, project)
594 def wiki_page_to_pdf(page, project)
578 595 pdf = ITCPDF.new(current_language)
579 596 pdf.SetTitle("#{project} - #{page.title}")
580 597 pdf.alias_nb_pages
581 598 pdf.footer_date = format_date(Date.today)
582 599 pdf.AddPage
583 600 pdf.SetFontStyle('B',11)
584 601 pdf.RDMMultiCell(190,5,
585 602 "#{project} - #{page.title} - # #{page.content.version}")
586 603 pdf.Ln
587 604 # Set resize image scale
588 605 pdf.SetImageScale(1.6)
589 606 pdf.SetFontStyle('',9)
607 write_wiki_page(pdf, page)
608 pdf.Output
609 end
610
611 def write_page_hierarchy(pdf, pages, node=nil, level=0)
612 if pages[node]
613 pages[node].each do |page|
614 if @new_page
615 pdf.AddPage
616 else
617 @new_page = true
618 end
619 pdf.Bookmark page.title, level
620 write_wiki_page(pdf, page)
621 write_page_hierarchy(pdf, pages, page.id, level + 1) if pages[page.id]
622 end
623 end
624 end
625
626 def write_wiki_page(pdf, page)
590 627 pdf.RDMwriteHTMLCell(190,5,0,0,
591 628 page.content.text.to_s, page.attachments, "TLRB")
592 629 if page.attachments.any?
593 630 pdf.Ln
594 631 pdf.SetFontStyle('B',9)
595 632 pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
596 633 pdf.Ln
597 634 for attachment in page.attachments
598 635 pdf.SetFontStyle('',8)
599 636 pdf.RDMCell(80,5, attachment.filename)
600 637 pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
601 638 pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
602 639 pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
603 640 pdf.Ln
604 641 end
605 642 end
606 pdf.Output
607 643 end
608 644
609 645 class RDMPdfEncoding
610 646 def self.rdm_from_utf8(txt, encoding)
611 647 txt ||= ''
612 648 txt = Redmine::CodesetUtil.from_utf8(txt, encoding)
613 649 if txt.respond_to?(:force_encoding)
614 650 txt.force_encoding('ASCII-8BIT')
615 651 end
616 652 txt
617 653 end
618 654
619 655 def self.attach(attachments, filename, encoding)
620 656 filename_utf8 = Redmine::CodesetUtil.to_utf8(filename, encoding)
621 657 atta = nil
622 658 if filename_utf8 =~ /^[^\/"]+\.(gif|jpg|jpe|jpeg|png)$/i
623 659 atta = Attachment.latest_attach(attachments, filename_utf8)
624 660 end
625 661 if atta && atta.readable? && atta.visible?
626 662 return atta
627 663 else
628 664 return nil
629 665 end
630 666 end
631 667 end
632 668 end
633 669 end
634 670 end
@@ -1,756 +1,769
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19 require 'wiki_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class WikiController; def rescue_action(e) raise e end; end
23 23
24 24 class WikiControllerTest < ActionController::TestCase
25 25 fixtures :projects, :users, :roles, :members, :member_roles,
26 26 :enabled_modules, :wikis, :wiki_pages, :wiki_contents,
27 27 :wiki_content_versions, :attachments
28 28
29 29 def setup
30 30 @controller = WikiController.new
31 31 @request = ActionController::TestRequest.new
32 32 @response = ActionController::TestResponse.new
33 33 User.current = nil
34 34 end
35 35
36 36 def test_show_start_page
37 37 get :show, :project_id => 'ecookbook'
38 38 assert_response :success
39 39 assert_template 'show'
40 40 assert_tag :tag => 'h1', :content => /CookBook documentation/
41 41
42 42 # child_pages macro
43 43 assert_tag :ul, :attributes => { :class => 'pages-hierarchy' },
44 44 :child => { :tag => 'li',
45 45 :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Page_with_an_inline_image' },
46 46 :content => 'Page with an inline image' } }
47 47 end
48 48
49 49 def test_export_link
50 50 Role.anonymous.add_permission! :export_wiki_pages
51 51 get :show, :project_id => 'ecookbook'
52 52 assert_response :success
53 53 assert_tag 'a', :attributes => {:href => '/projects/ecookbook/wiki/CookBook_documentation.txt'}
54 54 end
55 55
56 56 def test_show_page_with_name
57 57 get :show, :project_id => 1, :id => 'Another_page'
58 58 assert_response :success
59 59 assert_template 'show'
60 60 assert_tag :tag => 'h1', :content => /Another page/
61 61 # Included page with an inline image
62 62 assert_tag :tag => 'p', :content => /This is an inline image/
63 63 assert_tag :tag => 'img', :attributes => { :src => '/attachments/download/3',
64 64 :alt => 'This is a logo' }
65 65 end
66 66
67 67 def test_show_redirected_page
68 68 WikiRedirect.create!(:wiki_id => 1, :title => 'Old_title', :redirects_to => 'Another_page')
69 69
70 70 get :show, :project_id => 'ecookbook', :id => 'Old_title'
71 71 assert_redirected_to '/projects/ecookbook/wiki/Another_page'
72 72 end
73 73
74 74 def test_show_with_sidebar
75 75 page = Project.find(1).wiki.pages.new(:title => 'Sidebar')
76 76 page.content = WikiContent.new(:text => 'Side bar content for test_show_with_sidebar')
77 77 page.save!
78 78
79 79 get :show, :project_id => 1, :id => 'Another_page'
80 80 assert_response :success
81 81 assert_tag :tag => 'div', :attributes => {:id => 'sidebar'},
82 82 :content => /Side bar content for test_show_with_sidebar/
83 83 end
84 84
85 85 def test_show_should_display_section_edit_links
86 86 @request.session[:user_id] = 2
87 87 get :show, :project_id => 1, :id => 'Page with sections'
88 88 assert_no_tag 'a', :attributes => {
89 89 :href => '/projects/ecookbook/wiki/Page_with_sections/edit?section=1'
90 90 }
91 91 assert_tag 'a', :attributes => {
92 92 :href => '/projects/ecookbook/wiki/Page_with_sections/edit?section=2'
93 93 }
94 94 assert_tag 'a', :attributes => {
95 95 :href => '/projects/ecookbook/wiki/Page_with_sections/edit?section=3'
96 96 }
97 97 end
98 98
99 99 def test_show_current_version_should_display_section_edit_links
100 100 @request.session[:user_id] = 2
101 101 get :show, :project_id => 1, :id => 'Page with sections', :version => 3
102 102
103 103 assert_tag 'a', :attributes => {
104 104 :href => '/projects/ecookbook/wiki/Page_with_sections/edit?section=2'
105 105 }
106 106 end
107 107
108 108 def test_show_old_version_should_not_display_section_edit_links
109 109 @request.session[:user_id] = 2
110 110 get :show, :project_id => 1, :id => 'Page with sections', :version => 2
111 111
112 112 assert_no_tag 'a', :attributes => {
113 113 :href => '/projects/ecookbook/wiki/Page_with_sections/edit?section=2'
114 114 }
115 115 end
116 116
117 117 def test_show_unexistent_page_without_edit_right
118 118 get :show, :project_id => 1, :id => 'Unexistent page'
119 119 assert_response 404
120 120 end
121 121
122 122 def test_show_unexistent_page_with_edit_right
123 123 @request.session[:user_id] = 2
124 124 get :show, :project_id => 1, :id => 'Unexistent page'
125 125 assert_response :success
126 126 assert_template 'edit'
127 127 assert_no_tag 'input', :attributes => {:name => 'page[parent_id]'}
128 128 end
129 129
130 130 def test_show_unexistent_page_with_parent
131 131 @request.session[:user_id] = 2
132 132 get :show, :project_id => 1, :id => 'Unexistent page', :parent => 'Another_page'
133 133 assert_response :success
134 134 assert_template 'edit'
135 135 assert_tag 'input', :attributes => {:name => 'page[parent_id]', :value => '2'}
136 136 end
137 137
138 138 def test_show_should_not_show_history_without_permission
139 139 Role.anonymous.remove_permission! :view_wiki_edits
140 140 get :show, :project_id => 1, :id => 'Page with sections', :version => 2
141 141
142 142 assert_response 302
143 143 end
144 144
145 145 def test_create_page
146 146 @request.session[:user_id] = 2
147 147 assert_difference 'WikiPage.count' do
148 148 assert_difference 'WikiContent.count' do
149 149 put :update, :project_id => 1,
150 150 :id => 'New page',
151 151 :content => {:comments => 'Created the page',
152 152 :text => "h1. New page\n\nThis is a new page",
153 153 :version => 0}
154 154 end
155 155 end
156 156 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'New_page'
157 157 page = Project.find(1).wiki.find_page('New page')
158 158 assert !page.new_record?
159 159 assert_not_nil page.content
160 160 assert_nil page.parent
161 161 assert_equal 'Created the page', page.content.comments
162 162 end
163 163
164 164 def test_create_page_with_attachments
165 165 @request.session[:user_id] = 2
166 166 assert_difference 'WikiPage.count' do
167 167 assert_difference 'Attachment.count' do
168 168 put :update, :project_id => 1,
169 169 :id => 'New page',
170 170 :content => {:comments => 'Created the page',
171 171 :text => "h1. New page\n\nThis is a new page",
172 172 :version => 0},
173 173 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
174 174 end
175 175 end
176 176 page = Project.find(1).wiki.find_page('New page')
177 177 assert_equal 1, page.attachments.count
178 178 assert_equal 'testfile.txt', page.attachments.first.filename
179 179 end
180 180
181 181 def test_create_page_with_parent
182 182 @request.session[:user_id] = 2
183 183 assert_difference 'WikiPage.count' do
184 184 put :update, :project_id => 1, :id => 'New page',
185 185 :content => {:text => "h1. New page\n\nThis is a new page", :version => 0},
186 186 :page => {:parent_id => 2}
187 187 end
188 188 page = Project.find(1).wiki.find_page('New page')
189 189 assert_equal WikiPage.find(2), page.parent
190 190 end
191 191
192 192 def test_edit_page
193 193 @request.session[:user_id] = 2
194 194 get :edit, :project_id => 'ecookbook', :id => 'Another_page'
195 195
196 196 assert_response :success
197 197 assert_template 'edit'
198 198
199 199 assert_tag 'textarea',
200 200 :attributes => { :name => 'content[text]' },
201 201 :content => WikiPage.find_by_title('Another_page').content.text
202 202 end
203 203
204 204 def test_edit_section
205 205 @request.session[:user_id] = 2
206 206 get :edit, :project_id => 'ecookbook', :id => 'Page_with_sections', :section => 2
207 207
208 208 assert_response :success
209 209 assert_template 'edit'
210 210
211 211 page = WikiPage.find_by_title('Page_with_sections')
212 212 section, hash = Redmine::WikiFormatting::Textile::Formatter.new(page.content.text).get_section(2)
213 213
214 214 assert_tag 'textarea',
215 215 :attributes => { :name => 'content[text]' },
216 216 :content => section
217 217 assert_tag 'input',
218 218 :attributes => { :name => 'section', :type => 'hidden', :value => '2' }
219 219 assert_tag 'input',
220 220 :attributes => { :name => 'section_hash', :type => 'hidden', :value => hash }
221 221 end
222 222
223 223 def test_edit_invalid_section_should_respond_with_404
224 224 @request.session[:user_id] = 2
225 225 get :edit, :project_id => 'ecookbook', :id => 'Page_with_sections', :section => 10
226 226
227 227 assert_response 404
228 228 end
229 229
230 230 def test_update_page
231 231 @request.session[:user_id] = 2
232 232 assert_no_difference 'WikiPage.count' do
233 233 assert_no_difference 'WikiContent.count' do
234 234 assert_difference 'WikiContent::Version.count' do
235 235 put :update, :project_id => 1,
236 236 :id => 'Another_page',
237 237 :content => {
238 238 :comments => "my comments",
239 239 :text => "edited",
240 240 :version => 1
241 241 }
242 242 end
243 243 end
244 244 end
245 245 assert_redirected_to '/projects/ecookbook/wiki/Another_page'
246 246
247 247 page = Wiki.find(1).pages.find_by_title('Another_page')
248 248 assert_equal "edited", page.content.text
249 249 assert_equal 2, page.content.version
250 250 assert_equal "my comments", page.content.comments
251 251 end
252 252
253 253 def test_update_page_with_failure
254 254 @request.session[:user_id] = 2
255 255 assert_no_difference 'WikiPage.count' do
256 256 assert_no_difference 'WikiContent.count' do
257 257 assert_no_difference 'WikiContent::Version.count' do
258 258 put :update, :project_id => 1,
259 259 :id => 'Another_page',
260 260 :content => {
261 261 :comments => 'a' * 300, # failure here, comment is too long
262 262 :text => 'edited',
263 263 :version => 1
264 264 }
265 265 end
266 266 end
267 267 end
268 268 assert_response :success
269 269 assert_template 'edit'
270 270
271 271 assert_error_tag :descendant => {:content => /Comment is too long/}
272 272 assert_tag :tag => 'textarea', :attributes => {:id => 'content_text'}, :content => 'edited'
273 273 assert_tag :tag => 'input', :attributes => {:id => 'content_version', :value => '1'}
274 274 end
275 275
276 276 def test_update_page_with_attachments_only_should_not_create_content_version
277 277 @request.session[:user_id] = 2
278 278 assert_no_difference 'WikiPage.count' do
279 279 assert_no_difference 'WikiContent.count' do
280 280 assert_no_difference 'WikiContent::Version.count' do
281 281 assert_difference 'Attachment.count' do
282 282 put :update, :project_id => 1,
283 283 :id => 'Another_page',
284 284 :content => {
285 285 :comments => '',
286 286 :text => Wiki.find(1).find_page('Another_page').content.text,
287 287 :version => 1
288 288 },
289 289 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
290 290 end
291 291 end
292 292 end
293 293 end
294 294 end
295 295
296 296 def test_update_stale_page_should_not_raise_an_error
297 297 @request.session[:user_id] = 2
298 298 c = Wiki.find(1).find_page('Another_page').content
299 299 c.text = 'Previous text'
300 300 c.save!
301 301 assert_equal 2, c.version
302 302
303 303 assert_no_difference 'WikiPage.count' do
304 304 assert_no_difference 'WikiContent.count' do
305 305 assert_no_difference 'WikiContent::Version.count' do
306 306 put :update, :project_id => 1,
307 307 :id => 'Another_page',
308 308 :content => {
309 309 :comments => 'My comments',
310 310 :text => 'Text should not be lost',
311 311 :version => 1
312 312 }
313 313 end
314 314 end
315 315 end
316 316 assert_response :success
317 317 assert_template 'edit'
318 318 assert_tag :div,
319 319 :attributes => { :class => /error/ },
320 320 :content => /Data has been updated by another user/
321 321 assert_tag 'textarea',
322 322 :attributes => { :name => 'content[text]' },
323 323 :content => /Text should not be lost/
324 324 assert_tag 'input',
325 325 :attributes => { :name => 'content[comments]', :value => 'My comments' }
326 326
327 327 c.reload
328 328 assert_equal 'Previous text', c.text
329 329 assert_equal 2, c.version
330 330 end
331 331
332 332 def test_update_section
333 333 @request.session[:user_id] = 2
334 334 page = WikiPage.find_by_title('Page_with_sections')
335 335 section, hash = Redmine::WikiFormatting::Textile::Formatter.new(page.content.text).get_section(2)
336 336 text = page.content.text
337 337
338 338 assert_no_difference 'WikiPage.count' do
339 339 assert_no_difference 'WikiContent.count' do
340 340 assert_difference 'WikiContent::Version.count' do
341 341 put :update, :project_id => 1, :id => 'Page_with_sections',
342 342 :content => {
343 343 :text => "New section content",
344 344 :version => 3
345 345 },
346 346 :section => 2,
347 347 :section_hash => hash
348 348 end
349 349 end
350 350 end
351 351 assert_redirected_to '/projects/ecookbook/wiki/Page_with_sections'
352 352 assert_equal Redmine::WikiFormatting::Textile::Formatter.new(text).update_section(2, "New section content"), page.reload.content.text
353 353 end
354 354
355 355 def test_update_section_should_allow_stale_page_update
356 356 @request.session[:user_id] = 2
357 357 page = WikiPage.find_by_title('Page_with_sections')
358 358 section, hash = Redmine::WikiFormatting::Textile::Formatter.new(page.content.text).get_section(2)
359 359 text = page.content.text
360 360
361 361 assert_no_difference 'WikiPage.count' do
362 362 assert_no_difference 'WikiContent.count' do
363 363 assert_difference 'WikiContent::Version.count' do
364 364 put :update, :project_id => 1, :id => 'Page_with_sections',
365 365 :content => {
366 366 :text => "New section content",
367 367 :version => 2 # Current version is 3
368 368 },
369 369 :section => 2,
370 370 :section_hash => hash
371 371 end
372 372 end
373 373 end
374 374 assert_redirected_to '/projects/ecookbook/wiki/Page_with_sections'
375 375 page.reload
376 376 assert_equal Redmine::WikiFormatting::Textile::Formatter.new(text).update_section(2, "New section content"), page.content.text
377 377 assert_equal 4, page.content.version
378 378 end
379 379
380 380 def test_update_section_should_not_allow_stale_section_update
381 381 @request.session[:user_id] = 2
382 382
383 383 assert_no_difference 'WikiPage.count' do
384 384 assert_no_difference 'WikiContent.count' do
385 385 assert_no_difference 'WikiContent::Version.count' do
386 386 put :update, :project_id => 1, :id => 'Page_with_sections',
387 387 :content => {
388 388 :comments => 'My comments',
389 389 :text => "Text should not be lost",
390 390 :version => 3
391 391 },
392 392 :section => 2,
393 393 :section_hash => Digest::MD5.hexdigest("wrong hash")
394 394 end
395 395 end
396 396 end
397 397 assert_response :success
398 398 assert_template 'edit'
399 399 assert_tag :div,
400 400 :attributes => { :class => /error/ },
401 401 :content => /Data has been updated by another user/
402 402 assert_tag 'textarea',
403 403 :attributes => { :name => 'content[text]' },
404 404 :content => /Text should not be lost/
405 405 assert_tag 'input',
406 406 :attributes => { :name => 'content[comments]', :value => 'My comments' }
407 407 end
408 408
409 409 def test_preview
410 410 @request.session[:user_id] = 2
411 411 xhr :post, :preview, :project_id => 1, :id => 'CookBook_documentation',
412 412 :content => { :comments => '',
413 413 :text => 'this is a *previewed text*',
414 414 :version => 3 }
415 415 assert_response :success
416 416 assert_template 'common/_preview'
417 417 assert_tag :tag => 'strong', :content => /previewed text/
418 418 end
419 419
420 420 def test_preview_new_page
421 421 @request.session[:user_id] = 2
422 422 xhr :post, :preview, :project_id => 1, :id => 'New page',
423 423 :content => { :text => 'h1. New page',
424 424 :comments => '',
425 425 :version => 0 }
426 426 assert_response :success
427 427 assert_template 'common/_preview'
428 428 assert_tag :tag => 'h1', :content => /New page/
429 429 end
430 430
431 431 def test_history
432 432 get :history, :project_id => 1, :id => 'CookBook_documentation'
433 433 assert_response :success
434 434 assert_template 'history'
435 435 assert_not_nil assigns(:versions)
436 436 assert_equal 3, assigns(:versions).size
437 437 assert_select "input[type=submit][name=commit]"
438 438 end
439 439
440 440 def test_history_with_one_version
441 441 get :history, :project_id => 1, :id => 'Another_page'
442 442 assert_response :success
443 443 assert_template 'history'
444 444 assert_not_nil assigns(:versions)
445 445 assert_equal 1, assigns(:versions).size
446 446 assert_select "input[type=submit][name=commit]", false
447 447 end
448 448
449 449 def test_diff
450 450 get :diff, :project_id => 1, :id => 'CookBook_documentation', :version => 2, :version_from => 1
451 451 assert_response :success
452 452 assert_template 'diff'
453 453 assert_tag :tag => 'span', :attributes => { :class => 'diff_in'},
454 454 :content => /updated/
455 455 end
456 456
457 457 def test_annotate
458 458 get :annotate, :project_id => 1, :id => 'CookBook_documentation', :version => 2
459 459 assert_response :success
460 460 assert_template 'annotate'
461 461
462 462 # Line 1
463 463 assert_tag :tag => 'tr', :child => {
464 464 :tag => 'th', :attributes => {:class => 'line-num'}, :content => '1', :sibling => {
465 465 :tag => 'td', :attributes => {:class => 'author'}, :content => /John Smith/, :sibling => {
466 466 :tag => 'td', :content => /h1\. CookBook documentation/
467 467 }
468 468 }
469 469 }
470 470
471 471 # Line 5
472 472 assert_tag :tag => 'tr', :child => {
473 473 :tag => 'th', :attributes => {:class => 'line-num'}, :content => '5', :sibling => {
474 474 :tag => 'td', :attributes => {:class => 'author'}, :content => /redMine Admin/, :sibling => {
475 475 :tag => 'td', :content => /Some updated \[\[documentation\]\] here/
476 476 }
477 477 }
478 478 }
479 479 end
480 480
481 481 def test_get_rename
482 482 @request.session[:user_id] = 2
483 483 get :rename, :project_id => 1, :id => 'Another_page'
484 484 assert_response :success
485 485 assert_template 'rename'
486 486 assert_tag 'option',
487 487 :attributes => {:value => ''},
488 488 :content => '',
489 489 :parent => {:tag => 'select', :attributes => {:name => 'wiki_page[parent_id]'}}
490 490 assert_no_tag 'option',
491 491 :attributes => {:selected => 'selected'},
492 492 :parent => {:tag => 'select', :attributes => {:name => 'wiki_page[parent_id]'}}
493 493 end
494 494
495 495 def test_get_rename_child_page
496 496 @request.session[:user_id] = 2
497 497 get :rename, :project_id => 1, :id => 'Child_1'
498 498 assert_response :success
499 499 assert_template 'rename'
500 500 assert_tag 'option',
501 501 :attributes => {:value => ''},
502 502 :content => '',
503 503 :parent => {:tag => 'select', :attributes => {:name => 'wiki_page[parent_id]'}}
504 504 assert_tag 'option',
505 505 :attributes => {:value => '2', :selected => 'selected'},
506 506 :content => /Another page/,
507 507 :parent => {
508 508 :tag => 'select',
509 509 :attributes => {:name => 'wiki_page[parent_id]'}
510 510 }
511 511 end
512 512
513 513 def test_rename_with_redirect
514 514 @request.session[:user_id] = 2
515 515 post :rename, :project_id => 1, :id => 'Another_page',
516 516 :wiki_page => { :title => 'Another renamed page',
517 517 :redirect_existing_links => 1 }
518 518 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Another_renamed_page'
519 519 wiki = Project.find(1).wiki
520 520 # Check redirects
521 521 assert_not_nil wiki.find_page('Another page')
522 522 assert_nil wiki.find_page('Another page', :with_redirect => false)
523 523 end
524 524
525 525 def test_rename_without_redirect
526 526 @request.session[:user_id] = 2
527 527 post :rename, :project_id => 1, :id => 'Another_page',
528 528 :wiki_page => { :title => 'Another renamed page',
529 529 :redirect_existing_links => "0" }
530 530 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Another_renamed_page'
531 531 wiki = Project.find(1).wiki
532 532 # Check that there's no redirects
533 533 assert_nil wiki.find_page('Another page')
534 534 end
535 535
536 536 def test_rename_with_parent_assignment
537 537 @request.session[:user_id] = 2
538 538 post :rename, :project_id => 1, :id => 'Another_page',
539 539 :wiki_page => { :title => 'Another page', :redirect_existing_links => "0", :parent_id => '4' }
540 540 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Another_page'
541 541 assert_equal WikiPage.find(4), WikiPage.find_by_title('Another_page').parent
542 542 end
543 543
544 544 def test_rename_with_parent_unassignment
545 545 @request.session[:user_id] = 2
546 546 post :rename, :project_id => 1, :id => 'Child_1',
547 547 :wiki_page => { :title => 'Child 1', :redirect_existing_links => "0", :parent_id => '' }
548 548 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Child_1'
549 549 assert_nil WikiPage.find_by_title('Child_1').parent
550 550 end
551 551
552 552 def test_destroy_child
553 553 @request.session[:user_id] = 2
554 554 delete :destroy, :project_id => 1, :id => 'Child_1'
555 555 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
556 556 end
557 557
558 558 def test_destroy_parent
559 559 @request.session[:user_id] = 2
560 560 assert_no_difference('WikiPage.count') do
561 561 delete :destroy, :project_id => 1, :id => 'Another_page'
562 562 end
563 563 assert_response :success
564 564 assert_template 'destroy'
565 565 end
566 566
567 567 def test_destroy_parent_with_nullify
568 568 @request.session[:user_id] = 2
569 569 assert_difference('WikiPage.count', -1) do
570 570 delete :destroy, :project_id => 1, :id => 'Another_page', :todo => 'nullify'
571 571 end
572 572 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
573 573 assert_nil WikiPage.find_by_id(2)
574 574 end
575 575
576 576 def test_destroy_parent_with_cascade
577 577 @request.session[:user_id] = 2
578 578 assert_difference('WikiPage.count', -3) do
579 579 delete :destroy, :project_id => 1, :id => 'Another_page', :todo => 'destroy'
580 580 end
581 581 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
582 582 assert_nil WikiPage.find_by_id(2)
583 583 assert_nil WikiPage.find_by_id(5)
584 584 end
585 585
586 586 def test_destroy_parent_with_reassign
587 587 @request.session[:user_id] = 2
588 588 assert_difference('WikiPage.count', -1) do
589 589 delete :destroy, :project_id => 1, :id => 'Another_page', :todo => 'reassign', :reassign_to_id => 1
590 590 end
591 591 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
592 592 assert_nil WikiPage.find_by_id(2)
593 593 assert_equal WikiPage.find(1), WikiPage.find_by_id(5).parent
594 594 end
595 595
596 596 def test_index
597 597 get :index, :project_id => 'ecookbook'
598 598 assert_response :success
599 599 assert_template 'index'
600 600 pages = assigns(:pages)
601 601 assert_not_nil pages
602 602 assert_equal Project.find(1).wiki.pages.size, pages.size
603 603 assert_equal pages.first.content.updated_on, pages.first.updated_on
604 604
605 605 assert_tag :ul, :attributes => { :class => 'pages-hierarchy' },
606 606 :child => { :tag => 'li', :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/CookBook_documentation' },
607 607 :content => 'CookBook documentation' },
608 608 :child => { :tag => 'ul',
609 609 :child => { :tag => 'li',
610 610 :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Page_with_an_inline_image' },
611 611 :content => 'Page with an inline image' } } } },
612 612 :child => { :tag => 'li', :child => { :tag => 'a', :attributes => { :href => '/projects/ecookbook/wiki/Another_page' },
613 613 :content => 'Another page' } }
614 614 end
615 615
616 616 def test_index_should_include_atom_link
617 617 get :index, :project_id => 'ecookbook'
618 618 assert_tag 'a', :attributes => { :href => '/projects/ecookbook/activity.atom?show_wiki_edits=1'}
619 619 end
620 620
621 def test_export
621 def test_export_to_html
622 622 @request.session[:user_id] = 2
623 623 get :export, :project_id => 'ecookbook'
624 624
625 625 assert_response :success
626 626 assert_not_nil assigns(:pages)
627 assert assigns(:pages).any?
627 628 assert_equal "text/html", @response.content_type
628 629
629 630 assert_select "a[name=?]", "CookBook_documentation"
630 631 assert_select "a[name=?]", "Another_page"
631 632 assert_select "a[name=?]", "Page_with_an_inline_image"
632 633 end
633 634
634 def test_export_without_permission
635 def test_export_to_pdf
636 @request.session[:user_id] = 2
637 get :export, :project_id => 'ecookbook', :format => 'pdf'
638
639 assert_response :success
640 assert_not_nil assigns(:pages)
641 assert assigns(:pages).any?
642 assert_equal 'application/pdf', @response.content_type
643 assert_equal 'attachment; filename="ecookbook.pdf"', @response.headers['Content-Disposition']
644 assert @response.body.starts_with?('%PDF')
645 end
646
647 def test_export_without_permission_should_redirect
635 648 get :export, :project_id => 'ecookbook'
636 649
637 650 assert_response 302
638 651 end
639 652
640 653 def test_date_index
641 654 get :date_index, :project_id => 'ecookbook'
642 655
643 656 assert_response :success
644 657 assert_template 'date_index'
645 658 assert_not_nil assigns(:pages)
646 659 assert_not_nil assigns(:pages_by_date)
647 660
648 661 assert_tag 'a', :attributes => { :href => '/projects/ecookbook/activity.atom?show_wiki_edits=1'}
649 662 end
650 663
651 664 def test_not_found
652 665 get :show, :project_id => 999
653 666 assert_response 404
654 667 end
655 668
656 669 def test_protect_page
657 670 page = WikiPage.find_by_wiki_id_and_title(1, 'Another_page')
658 671 assert !page.protected?
659 672 @request.session[:user_id] = 2
660 673 post :protect, :project_id => 1, :id => page.title, :protected => '1'
661 674 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'Another_page'
662 675 assert page.reload.protected?
663 676 end
664 677
665 678 def test_unprotect_page
666 679 page = WikiPage.find_by_wiki_id_and_title(1, 'CookBook_documentation')
667 680 assert page.protected?
668 681 @request.session[:user_id] = 2
669 682 post :protect, :project_id => 1, :id => page.title, :protected => '0'
670 683 assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'CookBook_documentation'
671 684 assert !page.reload.protected?
672 685 end
673 686
674 687 def test_show_page_with_edit_link
675 688 @request.session[:user_id] = 2
676 689 get :show, :project_id => 1
677 690 assert_response :success
678 691 assert_template 'show'
679 692 assert_tag :tag => 'a', :attributes => { :href => '/projects/1/wiki/CookBook_documentation/edit' }
680 693 end
681 694
682 695 def test_show_page_without_edit_link
683 696 @request.session[:user_id] = 4
684 697 get :show, :project_id => 1
685 698 assert_response :success
686 699 assert_template 'show'
687 700 assert_no_tag :tag => 'a', :attributes => { :href => '/projects/1/wiki/CookBook_documentation/edit' }
688 701 end
689 702
690 703 def test_show_pdf
691 704 @request.session[:user_id] = 2
692 705 get :show, :project_id => 1, :format => 'pdf'
693 706 assert_response :success
694 707 assert_not_nil assigns(:page)
695 708 assert_equal 'application/pdf', @response.content_type
696 709 assert_equal 'attachment; filename="CookBook_documentation.pdf"',
697 710 @response.headers['Content-Disposition']
698 711 end
699 712
700 713 def test_show_html
701 714 @request.session[:user_id] = 2
702 715 get :show, :project_id => 1, :format => 'html'
703 716 assert_response :success
704 717 assert_not_nil assigns(:page)
705 718 assert_equal 'text/html', @response.content_type
706 719 assert_equal 'attachment; filename="CookBook_documentation.html"',
707 720 @response.headers['Content-Disposition']
708 721 end
709 722
710 723 def test_show_txt
711 724 @request.session[:user_id] = 2
712 725 get :show, :project_id => 1, :format => 'txt'
713 726 assert_response :success
714 727 assert_not_nil assigns(:page)
715 728 assert_equal 'text/plain', @response.content_type
716 729 assert_equal 'attachment; filename="CookBook_documentation.txt"',
717 730 @response.headers['Content-Disposition']
718 731 end
719 732
720 733 def test_edit_unprotected_page
721 734 # Non members can edit unprotected wiki pages
722 735 @request.session[:user_id] = 4
723 736 get :edit, :project_id => 1, :id => 'Another_page'
724 737 assert_response :success
725 738 assert_template 'edit'
726 739 end
727 740
728 741 def test_edit_protected_page_by_nonmember
729 742 # Non members can't edit protected wiki pages
730 743 @request.session[:user_id] = 4
731 744 get :edit, :project_id => 1, :id => 'CookBook_documentation'
732 745 assert_response 403
733 746 end
734 747
735 748 def test_edit_protected_page_by_member
736 749 @request.session[:user_id] = 2
737 750 get :edit, :project_id => 1, :id => 'CookBook_documentation'
738 751 assert_response :success
739 752 assert_template 'edit'
740 753 end
741 754
742 755 def test_history_of_non_existing_page_should_return_404
743 756 get :history, :project_id => 1, :id => 'Unknown_page'
744 757 assert_response 404
745 758 end
746 759
747 760 def test_add_attachment
748 761 @request.session[:user_id] = 2
749 762 assert_difference 'Attachment.count' do
750 763 post :add_attachment, :project_id => 1, :id => 'CookBook_documentation',
751 764 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
752 765 end
753 766 attachment = Attachment.first(:order => 'id DESC')
754 767 assert_equal Wiki.find(1).find_page('CookBook_documentation'), attachment.container
755 768 end
756 769 end
@@ -1,115 +1,124
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class RoutingWikiTest < ActionController::IntegrationTest
21 21 def test_wiki_matching
22 22 assert_routing(
23 23 { :method => 'get', :path => "/projects/567/wiki" },
24 24 { :controller => 'wiki', :action => 'show', :project_id => '567' }
25 25 )
26 26 assert_routing(
27 27 { :method => 'get', :path => "/projects/567/wiki/lalala" },
28 28 { :controller => 'wiki', :action => 'show', :project_id => '567',
29 29 :id => 'lalala' }
30 30 )
31 31 assert_routing(
32 { :method => 'get', :path => "/projects/567/wiki/lalala.pdf" },
33 { :controller => 'wiki', :action => 'show', :project_id => '567',
34 :id => 'lalala', :format => 'pdf' }
35 )
36 assert_routing(
32 37 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/diff" },
33 38 { :controller => 'wiki', :action => 'diff', :project_id => '1',
34 39 :id => 'CookBook_documentation' }
35 40 )
36 41 assert_routing(
37 42 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/diff/2" },
38 43 { :controller => 'wiki', :action => 'diff', :project_id => '1',
39 44 :id => 'CookBook_documentation', :version => '2' }
40 45 )
41 46 assert_routing(
42 47 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/diff/2/vs/1" },
43 48 { :controller => 'wiki', :action => 'diff', :project_id => '1',
44 49 :id => 'CookBook_documentation', :version => '2', :version_from => '1' }
45 50 )
46 51 assert_routing(
47 52 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/annotate/2" },
48 53 { :controller => 'wiki', :action => 'annotate', :project_id => '1',
49 54 :id => 'CookBook_documentation', :version => '2' }
50 55 )
51 56 end
52 57
53 58 def test_wiki_misc
54 59 assert_routing(
55 60 { :method => 'get', :path => "/projects/567/wiki/date_index" },
56 61 { :controller => 'wiki', :action => 'date_index', :project_id => '567' }
57 62 )
58 63 assert_routing(
59 64 { :method => 'get', :path => "/projects/567/wiki/export" },
60 65 { :controller => 'wiki', :action => 'export', :project_id => '567' }
61 66 )
62 67 assert_routing(
68 { :method => 'get', :path => "/projects/567/wiki/export.pdf" },
69 { :controller => 'wiki', :action => 'export', :project_id => '567', :format => 'pdf' }
70 )
71 assert_routing(
63 72 { :method => 'get', :path => "/projects/567/wiki/index" },
64 73 { :controller => 'wiki', :action => 'index', :project_id => '567' }
65 74 )
66 75 end
67 76
68 77 def test_wiki_resources
69 78 assert_routing(
70 79 { :method => 'get', :path => "/projects/567/wiki/my_page/edit" },
71 80 { :controller => 'wiki', :action => 'edit', :project_id => '567',
72 81 :id => 'my_page' }
73 82 )
74 83 assert_routing(
75 84 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/history" },
76 85 { :controller => 'wiki', :action => 'history', :project_id => '1',
77 86 :id => 'CookBook_documentation' }
78 87 )
79 88 assert_routing(
80 89 { :method => 'get', :path => "/projects/22/wiki/ladida/rename" },
81 90 { :controller => 'wiki', :action => 'rename', :project_id => '22',
82 91 :id => 'ladida' }
83 92 )
84 93 assert_routing(
85 94 { :method => 'post', :path => "/projects/567/wiki/CookBook_documentation/preview" },
86 95 { :controller => 'wiki', :action => 'preview', :project_id => '567',
87 96 :id => 'CookBook_documentation' }
88 97 )
89 98 assert_routing(
90 99 { :method => 'post', :path => "/projects/22/wiki/ladida/rename" },
91 100 { :controller => 'wiki', :action => 'rename', :project_id => '22',
92 101 :id => 'ladida' }
93 102 )
94 103 assert_routing(
95 104 { :method => 'post', :path => "/projects/22/wiki/ladida/protect" },
96 105 { :controller => 'wiki', :action => 'protect', :project_id => '22',
97 106 :id => 'ladida' }
98 107 )
99 108 assert_routing(
100 109 { :method => 'post', :path => "/projects/22/wiki/ladida/add_attachment" },
101 110 { :controller => 'wiki', :action => 'add_attachment', :project_id => '22',
102 111 :id => 'ladida' }
103 112 )
104 113 assert_routing(
105 114 { :method => 'put', :path => "/projects/567/wiki/my_page" },
106 115 { :controller => 'wiki', :action => 'update', :project_id => '567',
107 116 :id => 'my_page' }
108 117 )
109 118 assert_routing(
110 119 { :method => 'delete', :path => "/projects/22/wiki/ladida" },
111 120 { :controller => 'wiki', :action => 'destroy', :project_id => '22',
112 121 :id => 'ladida' }
113 122 )
114 123 end
115 124 end
General Comments 0
You need to be logged in to leave comments. Login now