wiki_controller.rb
354 lines
| 11.8 KiB
| text/x-ruby
|
RubyLexer
|
r4978 | # Redmine - project management software | ||
|
r9453 | # Copyright (C) 2006-2012 Jean-Philippe Lang | ||
|
r320 | # | ||
# This program is free software; you can redistribute it and/or | ||||
# modify it under the terms of the GNU General Public License | ||||
# as published by the Free Software Foundation; either version 2 | ||||
# of the License, or (at your option) any later version. | ||||
|
r5704 | # | ||
|
r320 | # This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
|
r5704 | # | ||
|
r320 | # You should have received a copy of the GNU General Public License | ||
# along with this program; if not, write to the Free Software | ||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
|
r580 | require 'diff' | ||
|
r4176 | # The WikiController follows the Rails REST controller pattern but with | ||
# a few differences | ||||
# | ||||
# * index - shows a list of WikiPages grouped by page or date | ||||
# * new - not used | ||||
# * create - not used | ||||
# * show - will also show the form for creating a new wiki page | ||||
# * edit - used to edit an existing or new page | ||||
# * update - used to save a wiki page update to the database, including new pages | ||||
# * destroy - normal | ||||
# | ||||
# Other member and collection methods are also used | ||||
# | ||||
# TODO: still being worked on | ||||
|
r320 | class WikiController < ApplicationController | ||
|
r2829 | default_search_scope :wiki_pages | ||
|
r663 | before_filter :find_wiki, :authorize | ||
|
r5302 | before_filter :find_existing_or_new_page, :only => [:show, :edit, :update] | ||
|
r10493 | before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy, :destroy_version] | ||
|
r10528 | accept_api_auth :index, :show, :update, :destroy | ||
|
r5704 | |||
|
r538 | helper :attachments | ||
|
r5704 | include AttachmentsHelper | ||
|
r2666 | helper :watchers | ||
|
r7621 | include Redmine::Export::PDF | ||
|
r4152 | |||
|
r4176 | # List of pages, sorted alphabetically and by parent (hierarchy) | ||
def index | ||||
|
r4978 | load_pages_for_index | ||
|
r10504 | |||
respond_to do |format| | ||||
format.html { | ||||
@pages_by_parent_id = @pages.group_by(&:parent_id) | ||||
} | ||||
format.api | ||||
end | ||||
|
r4978 | end | ||
|
r5704 | |||
|
r4978 | # List of page, by last update | ||
def date_index | ||||
load_pages_for_index | ||||
@pages_by_date = @pages.group_by {|p| p.updated_on.to_date} | ||||
|
r4176 | end | ||
|
r320 | # display a page (in editing mode if it doesn't exist) | ||
|
r4152 | def show | ||
|
r320 | if @page.new_record? | ||
|
r10504 | if User.current.allowed_to?(:edit_wiki_pages, @project) && editable? && !api_request? | ||
|
r812 | edit | ||
render :action => 'edit' | ||||
else | ||||
render_404 | ||||
end | ||||
return | ||||
|
r320 | end | ||
|
r1813 | if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project) | ||
|
r10504 | deny_access | ||
|
r1813 | return | ||
end | ||||
|
r421 | @content = @page.content_for_version(params[:version]) | ||
|
r3257 | if User.current.allowed_to?(:export_wiki_pages, @project) | ||
|
r7621 | if params[:format] == 'pdf' | ||
|
r8614 | send_data(wiki_page_to_pdf(@page, @project), :type => 'application/pdf', :filename => "#{@page.title}.pdf") | ||
|
r7621 | return | ||
elsif params[:format] == 'html' | ||||
|
r3257 | export = render_to_string :action => 'export', :layout => false | ||
send_data(export, :type => 'text/html', :filename => "#{@page.title}.html") | ||||
return | ||||
elsif params[:format] == 'txt' | ||||
send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt") | ||||
return | ||||
end | ||||
|
r320 | end | ||
|
r3257 | @editable = editable? | ||
|
r7711 | @sections_editable = @editable && User.current.allowed_to?(:edit_wiki_pages, @page.project) && | ||
|
r7852 | @content.current_version? && | ||
|
r7711 | Redmine::WikiFormatting.supports_section_edit? | ||
|
r10504 | respond_to do |format| | ||
format.html | ||||
format.api | ||||
end | ||||
|
r320 | end | ||
|
r5704 | |||
|
r320 | # edit an existing page or a new one | ||
def edit | ||||
|
r1400 | return render_403 unless editable? | ||
|
r8135 | if @page.new_record? | ||
@page.content = WikiContent.new(:page => @page) | ||||
if params[:parent].present? | ||||
@page.parent = @page.wiki.find_page(params[:parent].to_s) | ||||
end | ||||
end | ||||
|
r5704 | |||
|
r421 | @content = @page.content_for_version(params[:version]) | ||
|
r1953 | @content.text = initial_page_content(@page) if @content.text.blank? | ||
|
r320 | # don't keep previous comment | ||
|
r476 | @content.comments = nil | ||
|
r4158 | |||
# To prevent StaleObjectError exception when reverting to a previous version | ||||
@content.version = @page.content.version | ||||
|
r10063 | |||
|
r7709 | @text = @content.text | ||
|
r7711 | if params[:section].present? && Redmine::WikiFormatting.supports_section_edit? | ||
|
r7709 | @section = params[:section].to_i | ||
@text, @section_hash = Redmine::WikiFormatting.formatter.new(@text).get_section(@section) | ||||
render_404 if @text.blank? | ||||
end | ||||
|
r4158 | end | ||
# Creates a new page or updates an existing one | ||||
def update | ||||
return render_403 unless editable? | ||||
|
r10505 | was_new_page = @page.new_record? | ||
|
r4158 | @page.content = WikiContent.new(:page => @page) if @page.new_record? | ||
|
r8667 | @page.safe_attributes = params[:wiki_page] | ||
|
r5704 | |||
|
r10505 | @content = @page.content | ||
content_params = params[:content] | ||||
if content_params.nil? && params[:wiki_page].is_a?(Hash) | ||||
content_params = params[:wiki_page].slice(:text, :comments, :version) | ||||
end | ||||
content_params ||= {} | ||||
|
r4158 | |||
|
r10505 | @content.comments = content_params[:comments] | ||
@text = content_params[:text] | ||||
|
r7711 | if params[:section].present? && Redmine::WikiFormatting.supports_section_edit? | ||
|
r7709 | @section = params[:section].to_i | ||
@section_hash = params[:section_hash] | ||||
@content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(params[:section].to_i, @text, @section_hash) | ||||
else | ||||
|
r10505 | @content.version = content_params[:version] if content_params[:version] | ||
|
r7709 | @content.text = @text | ||
end | ||||
|
r4158 | @content.author = User.current | ||
|
r10615 | |||
if @page.save_with_content | ||||
|
r4158 | attachments = Attachment.attach_files(@page, params[:attachments]) | ||
render_attachment_warning_if_needed(@page) | ||||
call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page}) | ||||
|
r10505 | |||
respond_to do |format| | ||||
format.html { redirect_to :action => 'show', :project_id => @project, :id => @page.title } | ||||
format.api { | ||||
if was_new_page | ||||
render :action => 'show', :status => :created, :location => url_for(:controller => 'wiki', :action => 'show', :project_id => @project, :id => @page.title) | ||||
else | ||||
render_api_ok | ||||
end | ||||
} | ||||
end | ||||
|
r4315 | else | ||
|
r10505 | respond_to do |format| | ||
format.html { render :action => 'edit' } | ||||
format.api { render_validation_errors(@content) } | ||||
end | ||||
|
r320 | end | ||
|
r4158 | |||
|
r7709 | rescue ActiveRecord::StaleObjectError, Redmine::WikiFormatting::StaleSectionError | ||
|
r542 | # Optimistic locking exception | ||
|
r10505 | respond_to do |format| | ||
format.html { | ||||
flash.now[:error] = l(:notice_locking_conflict) | ||||
render :action => 'edit' | ||||
} | ||||
format.api { render_api_head :conflict } | ||||
end | ||||
|
r9346 | rescue ActiveRecord::RecordNotSaved | ||
|
r10505 | respond_to do |format| | ||
format.html { render :action => 'edit' } | ||||
format.api { render_validation_errors(@content) } | ||||
end | ||||
|
r320 | end | ||
|
r4158 | |||
|
r709 | # rename a page | ||
def rename | ||||
|
r2143 | return render_403 unless editable? | ||
|
r709 | @page.redirect_existing_links = true | ||
# used to display the *original* title if some AR validation errors occur | ||||
@original_title = @page.pretty_title | ||||
if request.post? && @page.update_attributes(params[:wiki_page]) | ||||
flash[:notice] = l(:notice_successful_update) | ||||
|
r4182 | redirect_to :action => 'show', :project_id => @project, :id => @page.title | ||
|
r709 | end | ||
end | ||||
|
r5704 | |||
|
r1400 | def protect | ||
|
r2143 | @page.update_attribute :protected, params[:protected] | ||
|
r4182 | redirect_to :action => 'show', :project_id => @project, :id => @page.title | ||
|
r1400 | end | ||
|
r320 | # show page history | ||
def history | ||||
|
r568 | @version_count = @page.content.versions.count | ||
|
r10529 | @version_pages = Paginator.new self, @version_count, per_page_option, params['page'] | ||
|
r5704 | # don't load text | ||
@versions = @page.content.versions.find :all, | ||||
|
r476 | :select => "id, author_id, comments, updated_on, version", | ||
|
r568 | :order => 'version DESC', | ||
|
r580 | :limit => @version_pages.items_per_page + 1, | ||
|
r568 | :offset => @version_pages.current.offset | ||
render :layout => false if request.xhr? | ||||
|
r320 | end | ||
|
r5704 | |||
|
r580 | def diff | ||
@diff = @page.diff(params[:version], params[:version_from]) | ||||
render_404 unless @diff | ||||
end | ||||
|
r5704 | |||
|
r1007 | def annotate | ||
@annotate = @page.annotate(params[:version]) | ||||
|
r2143 | render_404 unless @annotate | ||
|
r1007 | end | ||
|
r4181 | |||
|
r2584 | # Removes a wiki page and its history | ||
# Children can be either set as root pages, removed or reassigned to another parent page | ||||
|
r537 | def destroy | ||
|
r2143 | return render_403 unless editable? | ||
|
r5704 | |||
|
r2584 | @descendants_count = @page.descendants.size | ||
if @descendants_count > 0 | ||||
case params[:todo] | ||||
when 'nullify' | ||||
# Nothing to do | ||||
when 'destroy' | ||||
# Removes all its descendants | ||||
@page.descendants.each(&:destroy) | ||||
when 'reassign' | ||||
# Reassign children to another parent page | ||||
reassign_to = @wiki.pages.find_by_id(params[:reassign_to_id].to_i) | ||||
return unless reassign_to | ||||
@page.children.each do |child| | ||||
child.update_attribute(:parent, reassign_to) | ||||
end | ||||
else | ||||
@reassignable_to = @wiki.pages - @page.self_and_descendants | ||||
|
r10528 | # display the destroy form if it's a user request | ||
return unless api_request? | ||||
|
r2584 | end | ||
end | ||||
|
r2143 | @page.destroy | ||
|
r10528 | respond_to do |format| | ||
format.html { redirect_to :action => 'index', :project_id => @project } | ||||
format.api { render_api_ok } | ||||
end | ||||
|
r537 | end | ||
|
r320 | |||
|
r10493 | def destroy_version | ||
return render_403 unless editable? | ||||
@content = @page.content_for_version(params[:version]) | ||||
@content.destroy | ||||
redirect_to_referer_or :action => 'history', :id => @page.title, :project_id => @project | ||||
end | ||||
|
r8614 | # Export wiki to a single pdf or html file | ||
|
r4137 | def export | ||
|
r10614 | @pages = @wiki.pages.all(:order => 'title', :include => [:content, {:attachments => :author}]) | ||
|
r8614 | respond_to do |format| | ||
format.html { | ||||
export = render_to_string :action => 'export_multiple', :layout => false | ||||
send_data(export, :type => 'text/html', :filename => "wiki.html") | ||||
} | ||||
format.pdf { | ||||
send_data(wiki_pages_to_pdf(@pages, @project), :type => 'application/pdf', :filename => "#{@project.identifier}.pdf") | ||||
} | ||||
|
r4137 | end | ||
end | ||||
|
r5704 | |||
|
r320 | def preview | ||
|
r4182 | page = @wiki.find_page(params[:id]) | ||
|
r1431 | # page is nil when previewing a new page | ||
return render_403 unless page.nil? || editable?(page) | ||||
|
r1690 | if page | ||
@attachements = page.attachments | ||||
@previewed = page.content | ||||
end | ||||
|
r320 | @text = params[:content][:text] | ||
|
r801 | render :partial => 'common/preview' | ||
|
r320 | end | ||
|
r538 | def add_attachment | ||
|
r1400 | return render_403 unless editable? | ||
|
r3409 | attachments = Attachment.attach_files(@page, params[:attachments]) | ||
|
r3414 | render_attachment_warning_if_needed(@page) | ||
|
r4189 | redirect_to :action => 'show', :id => @page.title, :project_id => @project | ||
|
r538 | end | ||
|
r320 | private | ||
|
r5704 | |||
|
r320 | def find_wiki | ||
|
r4151 | @project = Project.find(params[:project_id]) | ||
|
r320 | @wiki = @project.wiki | ||
|
r561 | render_404 unless @wiki | ||
|
r320 | rescue ActiveRecord::RecordNotFound | ||
render_404 | ||||
end | ||||
|
r5704 | |||
|
r5302 | # Finds the requested page or a new page if it doesn't exist | ||
def find_existing_or_new_page | ||||
@page = @wiki.find_or_new_page(params[:id]) | ||||
|
r5303 | if @wiki.page_found_with_redirect? | ||
redirect_to params.update(:id => @page.title) | ||||
end | ||||
|
r5302 | end | ||
|
r5704 | |||
|
r2143 | # Finds the requested page and returns a 404 error if it doesn't exist | ||
def find_existing_page | ||||
|
r4182 | @page = @wiki.find_page(params[:id]) | ||
|
r5303 | if @page.nil? | ||
render_404 | ||||
return | ||||
end | ||||
if @wiki.page_found_with_redirect? | ||||
redirect_to params.update(:id => @page.title) | ||||
end | ||||
|
r2143 | end | ||
|
r5704 | |||
|
r1400 | # Returns true if the current user is allowed to edit the page, otherwise false | ||
def editable?(page = @page) | ||||
page.editable_by?(User.current) | ||||
end | ||||
|
r1953 | |||
# Returns the default content of a new wiki page | ||||
def initial_page_content(page) | ||||
helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting) | ||||
extend helper unless self.instance_of?(helper) | ||||
helper.instance_method(:initial_page_content).bind(self).call(page) | ||||
end | ||||
|
r5704 | |||
|
r4978 | def load_pages_for_index | ||
|
r10504 | @pages = @wiki.pages.with_updated_on.order("#{WikiPage.table_name}.title").includes(:wiki => :project).includes(:parent).all | ||
|
r4978 | end | ||
|
r320 | end | ||