File: app/controllers/wiki_controller.rb

Overview
Module Structure
Class Hierarchy
Code

Overview

Module Structure

  module: <Toplevel Module>
  class: WikiController#34
includes
  AttachmentsHelper   
  PDF ( Redmine::Export )
inherits from
  ApplicationController   
has properties
method: index #46
method: date_index #52
method: show #58
method: edit #96
method: update #122
method: rename #169
method: protect #180
method: history #186
method: diff #199
method: annotate #204
method: destroy #211
method: export #239
method: preview #252
method: add_attachment #264
method: find_wiki #273
method: find_existing_or_new_page #282
method: find_existing_page #290
method: editable? / 1 #302
method: initial_page_content / 1 #307
method: load_pages_for_index #313

Class Hierarchy

Code

   1  # Redmine - project management software
   2  # Copyright (C) 2006-2011  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]
  39 
  40    helper :attachments
  41    include AttachmentsHelper
  42    helper :watchers
  43    include Redmine::Export::PDF
  44 
  45    # List of pages, sorted alphabetically and by parent (hierarchy)
  46    def index
  47      load_pages_for_index
  48      @pages_by_parent_id = @pages.group_by(&:parent_id)
  49    end
  50 
  51    # List of page, by last update
  52    def date_index
  53      load_pages_for_index
  54      @pages_by_date = @pages.group_by {|p| p.updated_on.to_date}
  55    end
  56 
  57    # display a page (in editing mode if it doesn't exist)
  58    def show
  59      if @page.new_record?
  60        if User.current.allowed_to?(:edit_wiki_pages, @project) && editable?
  61          edit
  62          render :action => 'edit'
  63        else
  64          render_404
  65        end
  66        return
  67      end
  68      if params[:version] && !User.current.allowed_to?(:view_wiki_edits, @project)
  69        # Redirects user to the current version if he's not allowed to view previous versions
  70        redirect_to :version => nil
  71        return
  72      end
  73      @content = @page.content_for_version(params[:version])
  74      if User.current.allowed_to?(:export_wiki_pages, @project)
  75        if params[:format] == 'pdf'
  76          send_data(wiki_page_to_pdf(@page, @project), :type => 'application/pdf', :filename => "#{@page.title}.pdf")
  77          return
  78        elsif params[:format] == 'html'
  79          export = render_to_string :action => 'export', :layout => false
  80          send_data(export, :type => 'text/html', :filename => "#{@page.title}.html")
  81          return
  82        elsif params[:format] == 'txt'
  83          send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
  84          return
  85        end
  86      end
  87      @editable = editable?
  88      @sections_editable = @editable && User.current.allowed_to?(:edit_wiki_pages, @page.project) &&
  89        @content.current_version? &&
  90        Redmine::WikiFormatting.supports_section_edit?
  91 
  92      render :action => 'show'
  93    end
  94 
  95    # edit an existing page or a new one
  96    def edit
  97      return render_403 unless editable?
  98      if @page.new_record?
  99        @page.content = WikiContent.new(:page => @page)
 100        if params[:parent].present?
 101          @page.parent = @page.wiki.find_page(params[:parent].to_s)
 102        end
 103      end
 104 
 105      @content = @page.content_for_version(params[:version])
 106      @content.text = initial_page_content(@page) if @content.text.blank?
 107      # don't keep previous comment
 108      @content.comments = nil
 109 
 110      # To prevent StaleObjectError exception when reverting to a previous version
 111      @content.version = @page.content.version
 112      
 113      @text = @content.text
 114      if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
 115        @section = params[:section].to_i
 116        @text, @section_hash = Redmine::WikiFormatting.formatter.new(@text).get_section(@section)
 117        render_404 if @text.blank?
 118      end
 119    end
 120 
 121    # Creates a new page or updates an existing one
 122    def update
 123      return render_403 unless editable?
 124      @page.content = WikiContent.new(:page => @page) if @page.new_record?
 125      @page.safe_attributes = params[:wiki_page]
 126 
 127      @content = @page.content_for_version(params[:version])
 128      @content.text = initial_page_content(@page) if @content.text.blank?
 129      # don't keep previous comment
 130      @content.comments = nil
 131 
 132      if !@page.new_record? && params[:content].present? && @content.text == params[:content][:text]
 133        attachments = Attachment.attach_files(@page, params[:attachments])
 134        render_attachment_warning_if_needed(@page)
 135        # don't save content if text wasn't changed
 136        @page.save
 137        redirect_to :action => 'show', :project_id => @project, :id => @page.title
 138        return
 139      end
 140 
 141      @content.comments = params[:content][:comments]
 142      @text = params[:content][:text]
 143      if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
 144        @section = params[:section].to_i
 145        @section_hash = params[:section_hash]
 146        @content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(params[:section].to_i, @text, @section_hash)
 147      else
 148        @content.version = params[:content][:version]
 149        @content.text = @text
 150      end
 151      @content.author = User.current
 152      @page.content = @content
 153      if @page.save
 154        attachments = Attachment.attach_files(@page, params[:attachments])
 155        render_attachment_warning_if_needed(@page)
 156        call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
 157        redirect_to :action => 'show', :project_id => @project, :id => @page.title
 158      else
 159        render :action => 'edit'
 160      end
 161 
 162    rescue ActiveRecord::StaleObjectError, Redmine::WikiFormatting::StaleSectionError
 163      # Optimistic locking exception
 164      flash.now[:error] = l(:notice_locking_conflict)
 165      render :action => 'edit'
 166    end
 167 
 168    # rename a page
 169    def rename
 170      return render_403 unless editable?
 171      @page.redirect_existing_links = true
 172      # used to display the *original* title if some AR validation errors occur
 173      @original_title = @page.pretty_title
 174      if request.post? && @page.update_attributes(params[:wiki_page])
 175        flash[:notice] = l(:notice_successful_update)
 176        redirect_to :action => 'show', :project_id => @project, :id => @page.title
 177      end
 178    end
 179 
 180    def protect
 181      @page.update_attribute :protected, params[:protected]
 182      redirect_to :action => 'show', :project_id => @project, :id => @page.title
 183    end
 184 
 185    # show page history
 186    def history
 187      @version_count = @page.content.versions.count
 188      @version_pages = Paginator.new self, @version_count, per_page_option, params['p']
 189      # don't load text
 190      @versions = @page.content.versions.find :all,
 191                                              :select => "id, author_id, comments, updated_on, version",
 192                                              :order => 'version DESC',
 193                                              :limit  =>  @version_pages.items_per_page + 1,
 194                                              :offset =>  @version_pages.current.offset
 195 
 196      render :layout => false if request.xhr?
 197    end
 198 
 199    def diff
 200      @diff = @page.diff(params[:version], params[:version_from])
 201      render_404 unless @diff
 202    end
 203 
 204    def annotate
 205      @annotate = @page.annotate(params[:version])
 206      render_404 unless @annotate
 207    end
 208 
 209    # Removes a wiki page and its history
 210    # Children can be either set as root pages, removed or reassigned to another parent page
 211    def destroy
 212      return render_403 unless editable?
 213 
 214      @descendants_count = @page.descendants.size
 215      if @descendants_count > 0
 216        case params[:todo]
 217        when 'nullify'
 218          # Nothing to do
 219        when 'destroy'
 220          # Removes all its descendants
 221          @page.descendants.each(&:destroy)
 222        when 'reassign'
 223          # Reassign children to another parent page
 224          reassign_to = @wiki.pages.find_by_id(params[:reassign_to_id].to_i)
 225          return unless reassign_to
 226          @page.children.each do |child|
 227            child.update_attribute(:parent, reassign_to)
 228          end
 229        else
 230          @reassignable_to = @wiki.pages - @page.self_and_descendants
 231          return
 232        end
 233      end
 234      @page.destroy
 235      redirect_to :action => 'index', :project_id => @project
 236    end
 237 
 238    # Export wiki to a single pdf or html file
 239    def export
 240      @pages = @wiki.pages.all(:order => 'title', :include => [:content, :attachments], :limit => 75)
 241      respond_to do |format|
 242        format.html {
 243          export = render_to_string :action => 'export_multiple', :layout => false
 244          send_data(export, :type => 'text/html', :filename => "wiki.html")
 245        }
 246        format.pdf {
 247          send_data(wiki_pages_to_pdf(@pages, @project), :type => 'application/pdf', :filename => "#{@project.identifier}.pdf")
 248        }
 249      end
 250    end
 251 
 252    def preview
 253      page = @wiki.find_page(params[:id])
 254      # page is nil when previewing a new page
 255      return render_403 unless page.nil? || editable?(page)
 256      if page
 257        @attachements = page.attachments
 258        @previewed = page.content
 259      end
 260      @text = params[:content][:text]
 261      render :partial => 'common/preview'
 262    end
 263 
 264    def add_attachment
 265      return render_403 unless editable?
 266      attachments = Attachment.attach_files(@page, params[:attachments])
 267      render_attachment_warning_if_needed(@page)
 268      redirect_to :action => 'show', :id => @page.title, :project_id => @project
 269    end
 270 
 271  private
 272 
 273    def find_wiki
 274      @project = Project.find(params[:project_id])
 275      @wiki = @project.wiki
 276      render_404 unless @wiki
 277    rescue ActiveRecord::RecordNotFound
 278      render_404
 279    end
 280 
 281    # Finds the requested page or a new page if it doesn't exist
 282    def find_existing_or_new_page
 283      @page = @wiki.find_or_new_page(params[:id])
 284      if @wiki.page_found_with_redirect?
 285        redirect_to params.update(:id => @page.title)
 286      end
 287    end
 288 
 289    # Finds the requested page and returns a 404 error if it doesn't exist
 290    def find_existing_page
 291      @page = @wiki.find_page(params[:id])
 292      if @page.nil?
 293        render_404
 294        return
 295      end
 296      if @wiki.page_found_with_redirect?
 297        redirect_to params.update(:id => @page.title)
 298      end
 299    end
 300 
 301    # Returns true if the current user is allowed to edit the page, otherwise false
 302    def editable?(page = @page)
 303      page.editable_by?(User.current)
 304    end
 305 
 306    # Returns the default content of a new wiki page
 307    def initial_page_content(page)
 308      helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
 309      extend helper unless self.instance_of?(helper)
 310      helper.instance_method(:initial_page_content).bind(self).call(page)
 311    end
 312 
 313    def load_pages_for_index
 314      @pages = @wiki.pages.with_updated_on.all(:order => 'title', :include => {:wiki => :project})
 315    end
 316  end