To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
root / .svn / pristine / 17 / 1786faee3d0cf0b377ade4249d7c22ac019c3500.svn-base @ 1298:4f746d8966dd
History | View | Annotate | Download (11.6 KB)
| 1 | 1295:622f24f53b42 | Chris | # Redmine - project management software |
|---|---|---|---|
| 2 | # Copyright (C) 2006-2013 Jean-Philippe Lang |
||
| 3 | # |
||
| 4 | # This program is free software; you can redistribute it and/or |
||
| 5 | # modify it under the terms of the GNU General Public License |
||
| 6 | # as published by the Free Software Foundation; either version 2 |
||
| 7 | # of the License, or (at your option) any later version. |
||
| 8 | # |
||
| 9 | # This program is distributed in the hope that it will be useful, |
||
| 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
| 12 | # GNU General Public License for more details. |
||
| 13 | # |
||
| 14 | # You should have received a copy of the GNU General Public License |
||
| 15 | # along with this program; if not, write to the Free Software |
||
| 16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
| 17 | |||
| 18 | require 'diff' |
||
| 19 | |||
| 20 | # The WikiController follows the Rails REST controller pattern but with |
||
| 21 | # a few differences |
||
| 22 | # |
||
| 23 | # * index - shows a list of WikiPages grouped by page or date |
||
| 24 | # * new - not used |
||
| 25 | # * create - not used |
||
| 26 | # * show - will also show the form for creating a new wiki page |
||
| 27 | # * edit - used to edit an existing or new page |
||
| 28 | # * update - used to save a wiki page update to the database, including new pages |
||
| 29 | # * destroy - normal |
||
| 30 | # |
||
| 31 | # Other member and collection methods are also used |
||
| 32 | # |
||
| 33 | # TODO: still being worked on |
||
| 34 | class WikiController < ApplicationController |
||
| 35 | default_search_scope :wiki_pages |
||
| 36 | before_filter :find_wiki, :authorize |
||
| 37 | before_filter :find_existing_or_new_page, :only => [:show, :edit, :update] |
||
| 38 | before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy, :destroy_version] |
||
| 39 | accept_api_auth :index, :show, :update, :destroy |
||
| 40 | before_filter :find_attachments, :only => [:preview] |
||
| 41 | |||
| 42 | helper :attachments |
||
| 43 | include AttachmentsHelper |
||
| 44 | helper :watchers |
||
| 45 | include Redmine::Export::PDF |
||
| 46 | |||
| 47 | # List of pages, sorted alphabetically and by parent (hierarchy) |
||
| 48 | def index |
||
| 49 | load_pages_for_index |
||
| 50 | |||
| 51 | respond_to do |format| |
||
| 52 | format.html {
|
||
| 53 | @pages_by_parent_id = @pages.group_by(&:parent_id) |
||
| 54 | } |
||
| 55 | format.api |
||
| 56 | end |
||
| 57 | end |
||
| 58 | |||
| 59 | # List of page, by last update |
||
| 60 | def date_index |
||
| 61 | load_pages_for_index |
||
| 62 | @pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
|
||
| 63 | end |
||
| 64 | |||
| 65 | # display a page (in editing mode if it doesn't exist) |
||
| 66 | def show |
||
| 67 | if @page.new_record? |
||
| 68 | if User.current.allowed_to?(:edit_wiki_pages, @project) && editable? && !api_request? |
||
| 69 | edit |
||
| 70 | render :action => 'edit' |
||
| 71 | else |
||
| 72 | render_404 |
||
| 73 | end |
||
| 74 | return |
||
| 75 | end |
||
| 76 | if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project) |
||
| 77 | deny_access |
||
| 78 | return |
||
| 79 | end |
||
| 80 | @content = @page.content_for_version(params[:version]) |
||
| 81 | if User.current.allowed_to?(:export_wiki_pages, @project) |
||
| 82 | if params[:format] == 'pdf' |
||
| 83 | send_data(wiki_page_to_pdf(@page, @project), :type => 'application/pdf', :filename => "#{@page.title}.pdf")
|
||
| 84 | return |
||
| 85 | elsif params[:format] == 'html' |
||
| 86 | export = render_to_string :action => 'export', :layout => false |
||
| 87 | send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
|
||
| 88 | return |
||
| 89 | elsif params[:format] == 'txt' |
||
| 90 | send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
|
||
| 91 | return |
||
| 92 | end |
||
| 93 | end |
||
| 94 | @editable = editable? |
||
| 95 | @sections_editable = @editable && User.current.allowed_to?(:edit_wiki_pages, @page.project) && |
||
| 96 | @content.current_version? && |
||
| 97 | Redmine::WikiFormatting.supports_section_edit? |
||
| 98 | |||
| 99 | respond_to do |format| |
||
| 100 | format.html |
||
| 101 | format.api |
||
| 102 | end |
||
| 103 | end |
||
| 104 | |||
| 105 | # edit an existing page or a new one |
||
| 106 | def edit |
||
| 107 | return render_403 unless editable? |
||
| 108 | if @page.new_record? |
||
| 109 | @page.content = WikiContent.new(:page => @page) |
||
| 110 | if params[:parent].present? |
||
| 111 | @page.parent = @page.wiki.find_page(params[:parent].to_s) |
||
| 112 | end |
||
| 113 | end |
||
| 114 | |||
| 115 | @content = @page.content_for_version(params[:version]) |
||
| 116 | @content.text = initial_page_content(@page) if @content.text.blank? |
||
| 117 | # don't keep previous comment |
||
| 118 | @content.comments = nil |
||
| 119 | |||
| 120 | # To prevent StaleObjectError exception when reverting to a previous version |
||
| 121 | @content.version = @page.content.version |
||
| 122 | |||
| 123 | @text = @content.text |
||
| 124 | if params[:section].present? && Redmine::WikiFormatting.supports_section_edit? |
||
| 125 | @section = params[:section].to_i |
||
| 126 | @text, @section_hash = Redmine::WikiFormatting.formatter.new(@text).get_section(@section) |
||
| 127 | render_404 if @text.blank? |
||
| 128 | end |
||
| 129 | end |
||
| 130 | |||
| 131 | # Creates a new page or updates an existing one |
||
| 132 | def update |
||
| 133 | return render_403 unless editable? |
||
| 134 | was_new_page = @page.new_record? |
||
| 135 | @page.content = WikiContent.new(:page => @page) if @page.new_record? |
||
| 136 | @page.safe_attributes = params[:wiki_page] |
||
| 137 | |||
| 138 | @content = @page.content |
||
| 139 | content_params = params[:content] |
||
| 140 | if content_params.nil? && params[:wiki_page].is_a?(Hash) |
||
| 141 | content_params = params[:wiki_page].slice(:text, :comments, :version) |
||
| 142 | end |
||
| 143 | content_params ||= {}
|
||
| 144 | |||
| 145 | @content.comments = content_params[:comments] |
||
| 146 | @text = content_params[:text] |
||
| 147 | if params[:section].present? && Redmine::WikiFormatting.supports_section_edit? |
||
| 148 | @section = params[:section].to_i |
||
| 149 | @section_hash = params[:section_hash] |
||
| 150 | @content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(params[:section].to_i, @text, @section_hash) |
||
| 151 | else |
||
| 152 | @content.version = content_params[:version] if content_params[:version] |
||
| 153 | @content.text = @text |
||
| 154 | end |
||
| 155 | @content.author = User.current |
||
| 156 | |||
| 157 | if @page.save_with_content |
||
| 158 | attachments = Attachment.attach_files(@page, params[:attachments]) |
||
| 159 | render_attachment_warning_if_needed(@page) |
||
| 160 | call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
|
||
| 161 | |||
| 162 | respond_to do |format| |
||
| 163 | format.html { redirect_to project_wiki_page_path(@project, @page.title) }
|
||
| 164 | format.api {
|
||
| 165 | if was_new_page |
||
| 166 | render :action => 'show', :status => :created, :location => project_wiki_page_path(@project, @page.title) |
||
| 167 | else |
||
| 168 | render_api_ok |
||
| 169 | end |
||
| 170 | } |
||
| 171 | end |
||
| 172 | else |
||
| 173 | respond_to do |format| |
||
| 174 | format.html { render :action => 'edit' }
|
||
| 175 | format.api { render_validation_errors(@content) }
|
||
| 176 | end |
||
| 177 | end |
||
| 178 | |||
| 179 | rescue ActiveRecord::StaleObjectError, Redmine::WikiFormatting::StaleSectionError |
||
| 180 | # Optimistic locking exception |
||
| 181 | respond_to do |format| |
||
| 182 | format.html {
|
||
| 183 | flash.now[:error] = l(:notice_locking_conflict) |
||
| 184 | render :action => 'edit' |
||
| 185 | } |
||
| 186 | format.api { render_api_head :conflict }
|
||
| 187 | end |
||
| 188 | rescue ActiveRecord::RecordNotSaved |
||
| 189 | respond_to do |format| |
||
| 190 | format.html { render :action => 'edit' }
|
||
| 191 | format.api { render_validation_errors(@content) }
|
||
| 192 | end |
||
| 193 | end |
||
| 194 | |||
| 195 | # rename a page |
||
| 196 | def rename |
||
| 197 | return render_403 unless editable? |
||
| 198 | @page.redirect_existing_links = true |
||
| 199 | # used to display the *original* title if some AR validation errors occur |
||
| 200 | @original_title = @page.pretty_title |
||
| 201 | if request.post? && @page.update_attributes(params[:wiki_page]) |
||
| 202 | flash[:notice] = l(:notice_successful_update) |
||
| 203 | redirect_to project_wiki_page_path(@project, @page.title) |
||
| 204 | end |
||
| 205 | end |
||
| 206 | |||
| 207 | def protect |
||
| 208 | @page.update_attribute :protected, params[:protected] |
||
| 209 | redirect_to project_wiki_page_path(@project, @page.title) |
||
| 210 | end |
||
| 211 | |||
| 212 | # show page history |
||
| 213 | def history |
||
| 214 | @version_count = @page.content.versions.count |
||
| 215 | @version_pages = Paginator.new @version_count, per_page_option, params['page'] |
||
| 216 | # don't load text |
||
| 217 | @versions = @page.content.versions. |
||
| 218 | select("id, author_id, comments, updated_on, version").
|
||
| 219 | reorder('version DESC').
|
||
| 220 | limit(@version_pages.per_page + 1). |
||
| 221 | offset(@version_pages.offset). |
||
| 222 | all |
||
| 223 | |||
| 224 | render :layout => false if request.xhr? |
||
| 225 | end |
||
| 226 | |||
| 227 | def diff |
||
| 228 | @diff = @page.diff(params[:version], params[:version_from]) |
||
| 229 | render_404 unless @diff |
||
| 230 | end |
||
| 231 | |||
| 232 | def annotate |
||
| 233 | @annotate = @page.annotate(params[:version]) |
||
| 234 | render_404 unless @annotate |
||
| 235 | end |
||
| 236 | |||
| 237 | # Removes a wiki page and its history |
||
| 238 | # Children can be either set as root pages, removed or reassigned to another parent page |
||
| 239 | def destroy |
||
| 240 | return render_403 unless editable? |
||
| 241 | |||
| 242 | @descendants_count = @page.descendants.size |
||
| 243 | if @descendants_count > 0 |
||
| 244 | case params[:todo] |
||
| 245 | when 'nullify' |
||
| 246 | # Nothing to do |
||
| 247 | when 'destroy' |
||
| 248 | # Removes all its descendants |
||
| 249 | @page.descendants.each(&:destroy) |
||
| 250 | when 'reassign' |
||
| 251 | # Reassign children to another parent page |
||
| 252 | reassign_to = @wiki.pages.find_by_id(params[:reassign_to_id].to_i) |
||
| 253 | return unless reassign_to |
||
| 254 | @page.children.each do |child| |
||
| 255 | child.update_attribute(:parent, reassign_to) |
||
| 256 | end |
||
| 257 | else |
||
| 258 | @reassignable_to = @wiki.pages - @page.self_and_descendants |
||
| 259 | # display the destroy form if it's a user request |
||
| 260 | return unless api_request? |
||
| 261 | end |
||
| 262 | end |
||
| 263 | @page.destroy |
||
| 264 | respond_to do |format| |
||
| 265 | format.html { redirect_to project_wiki_index_path(@project) }
|
||
| 266 | format.api { render_api_ok }
|
||
| 267 | end |
||
| 268 | end |
||
| 269 | |||
| 270 | def destroy_version |
||
| 271 | return render_403 unless editable? |
||
| 272 | |||
| 273 | @content = @page.content_for_version(params[:version]) |
||
| 274 | @content.destroy |
||
| 275 | redirect_to_referer_or history_project_wiki_page_path(@project, @page.title) |
||
| 276 | end |
||
| 277 | |||
| 278 | # Export wiki to a single pdf or html file |
||
| 279 | def export |
||
| 280 | @pages = @wiki.pages.all(:order => 'title', :include => [:content, {:attachments => :author}])
|
||
| 281 | respond_to do |format| |
||
| 282 | format.html {
|
||
| 283 | export = render_to_string :action => 'export_multiple', :layout => false |
||
| 284 | send_data(export, :type => 'text/html', :filename => "wiki.html") |
||
| 285 | } |
||
| 286 | format.pdf {
|
||
| 287 | send_data(wiki_pages_to_pdf(@pages, @project), :type => 'application/pdf', :filename => "#{@project.identifier}.pdf")
|
||
| 288 | } |
||
| 289 | end |
||
| 290 | end |
||
| 291 | |||
| 292 | def preview |
||
| 293 | page = @wiki.find_page(params[:id]) |
||
| 294 | # page is nil when previewing a new page |
||
| 295 | return render_403 unless page.nil? || editable?(page) |
||
| 296 | if page |
||
| 297 | @attachments += page.attachments |
||
| 298 | @previewed = page.content |
||
| 299 | end |
||
| 300 | @text = params[:content][:text] |
||
| 301 | render :partial => 'common/preview' |
||
| 302 | end |
||
| 303 | |||
| 304 | def add_attachment |
||
| 305 | return render_403 unless editable? |
||
| 306 | attachments = Attachment.attach_files(@page, params[:attachments]) |
||
| 307 | render_attachment_warning_if_needed(@page) |
||
| 308 | redirect_to :action => 'show', :id => @page.title, :project_id => @project |
||
| 309 | end |
||
| 310 | |||
| 311 | private |
||
| 312 | |||
| 313 | def find_wiki |
||
| 314 | @project = Project.find(params[:project_id]) |
||
| 315 | @wiki = @project.wiki |
||
| 316 | render_404 unless @wiki |
||
| 317 | rescue ActiveRecord::RecordNotFound |
||
| 318 | render_404 |
||
| 319 | end |
||
| 320 | |||
| 321 | # Finds the requested page or a new page if it doesn't exist |
||
| 322 | def find_existing_or_new_page |
||
| 323 | @page = @wiki.find_or_new_page(params[:id]) |
||
| 324 | if @wiki.page_found_with_redirect? |
||
| 325 | redirect_to params.update(:id => @page.title) |
||
| 326 | end |
||
| 327 | end |
||
| 328 | |||
| 329 | # Finds the requested page and returns a 404 error if it doesn't exist |
||
| 330 | def find_existing_page |
||
| 331 | @page = @wiki.find_page(params[:id]) |
||
| 332 | if @page.nil? |
||
| 333 | render_404 |
||
| 334 | return |
||
| 335 | end |
||
| 336 | if @wiki.page_found_with_redirect? |
||
| 337 | redirect_to params.update(:id => @page.title) |
||
| 338 | end |
||
| 339 | end |
||
| 340 | |||
| 341 | # Returns true if the current user is allowed to edit the page, otherwise false |
||
| 342 | def editable?(page = @page) |
||
| 343 | page.editable_by?(User.current) |
||
| 344 | end |
||
| 345 | |||
| 346 | # Returns the default content of a new wiki page |
||
| 347 | def initial_page_content(page) |
||
| 348 | helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting) |
||
| 349 | extend helper unless self.instance_of?(helper) |
||
| 350 | helper.instance_method(:initial_page_content).bind(self).call(page) |
||
| 351 | end |
||
| 352 | |||
| 353 | def load_pages_for_index |
||
| 354 | @pages = @wiki.pages.with_updated_on.reorder("#{WikiPage.table_name}.title").includes(:wiki => :project).includes(:parent).all
|
||
| 355 | end |
||
| 356 | end |