OSDN Git Service

Wiki page protection (#851, patch #1146 by Mateo Murphy with slight changes).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 4 May 2008 15:05:38 +0000 (15:05 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 4 May 2008 15:05:38 +0000 (15:05 +0000)
New permission added: protect wiki pages. Once a page is protected, it can be edited/renamed/deleted only by users who have this permission.

git-svn-id: http://redmine.rubyforge.org/svn/trunk@1415 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/controllers/wiki_controller.rb
app/models/wiki_page.rb
app/views/wiki/show.rhtml
db/migrate/093_add_wiki_pages_protected.rb [new file with mode: 0644]
lib/redmine.rb
public/images/locked.png
public/images/unlock.png
test/fixtures/roles.yml
test/fixtures/wiki_pages.yml
test/functional/wiki_controller_test.rb

index 53c5ec5..44113eb 100644 (file)
@@ -21,7 +21,7 @@ class WikiController < ApplicationController
   layout 'base'
   before_filter :find_wiki, :authorize
   
-  verify :method => :post, :only => [:destroy, :destroy_attachment], :redirect_to => { :action => :index }
+  verify :method => :post, :only => [:destroy, :destroy_attachment, :protect], :redirect_to => { :action => :index }
 
   helper :attachments
   include AttachmentsHelper   
@@ -48,12 +48,14 @@ class WikiController < ApplicationController
       send_data(@content.text, :type => 'text/plain', :filename => "#{@page.title}.txt")
       return
     end
+       @editable = editable?
     render :action => 'show'
   end
   
   # edit an existing page or a new one
   def edit
     @page = @wiki.find_or_new_page(params[:page])    
+    return render_403 unless editable?
     @page.content = WikiContent.new(:page => @page) if @page.new_record?
     
     @content = @page.content_for_version(params[:version])
@@ -82,7 +84,8 @@ class WikiController < ApplicationController
   
   # rename a page
   def rename
-    @page = @wiki.find_page(params[:page])    
+    @page = @wiki.find_page(params[:page])
+       return render_403 unless editable?
     @page.redirect_existing_links = true
     # used to display the *original* title if some AR validation errors occur
     @original_title = @page.pretty_title
@@ -92,6 +95,12 @@ class WikiController < ApplicationController
     end
   end
   
+  def protect
+    page = @wiki.find_page(params[:page])
+    page.update_attribute :protected, params[:protected]
+    redirect_to :action => 'index', :id => @project, :page => page.title
+  end
+
   # show page history
   def history
     @page = @wiki.find_page(params[:page])
@@ -122,6 +131,7 @@ class WikiController < ApplicationController
   # remove a wiki page and its history
   def destroy
     @page = @wiki.find_page(params[:page])
+       return render_403 unless editable?
     @page.destroy if @page
     redirect_to :action => 'special', :id => @project, :page => 'Page_index'
   end
@@ -152,6 +162,7 @@ class WikiController < ApplicationController
   
   def preview
     page = @wiki.find_page(params[:page])
+    return render_403 unless editable?(page)
     @attachements = page.attachments if page
     @text = params[:content][:text]
     render :partial => 'common/preview'
@@ -159,12 +170,14 @@ class WikiController < ApplicationController
 
   def add_attachment
     @page = @wiki.find_page(params[:page])
+    return render_403 unless editable?
     attach_files(@page, params[:attachments])
     redirect_to :action => 'index', :page => @page.title
   end
 
   def destroy_attachment
     @page = @wiki.find_page(params[:page])
+    return render_403 unless editable?
     @page.attachments.find(params[:attachment_id]).destroy
     redirect_to :action => 'index', :page => @page.title
   end
@@ -178,4 +191,9 @@ private
   rescue ActiveRecord::RecordNotFound
     render_404
   end
+  
+  # Returns true if the current user is allowed to edit the page, otherwise false
+  def editable?(page = @page)
+    page.editable_by?(User.current)
+  end
 end
index 8ce71cb..95750f3 100644 (file)
@@ -105,6 +105,11 @@ class WikiPage < ActiveRecord::Base
   def text
     content.text if content
   end
+  
+  # Returns true if usr is allowed to edit the page, otherwise false
+  def editable_by?(usr)
+    !protected? || usr.allowed_to?(:protect_wiki_pages, wiki.project)
+  end
 end
 
 class WikiDiff
index e4413d0..8092525 100644 (file)
@@ -1,8 +1,12 @@
 <div class="contextual">
+<% if @editable %>
 <%= link_to_if_authorized(l(:button_edit), {:action => 'edit', :page => @page.title}, :class => 'icon icon-edit', :accesskey => accesskey(:edit)) if @content.version == @page.content.version %>
+<%= link_to_if_authorized(l(:button_lock), {:action => 'protect', :page => @page.title, :protected => 1}, :method => :post, :class => 'icon icon-lock') if !@page.protected? %>
+<%= link_to_if_authorized(l(:button_unlock), {:action => 'protect', :page => @page.title, :protected => 0}, :method => :post, :class => 'icon icon-unlock') if @page.protected? %>
 <%= link_to_if_authorized(l(:button_rename), {:action => 'rename', :page => @page.title}, :class => 'icon icon-move') if @content.version == @page.content.version %>
 <%= link_to_if_authorized(l(:button_delete), {:action => 'destroy', :page => @page.title}, :method => :post, :confirm => l(:text_are_you_sure), :class => 'icon icon-del') %>
 <%= link_to_if_authorized(l(:button_rollback), {:action => 'edit', :page => @page.title, :version => @content.version }, :class => 'icon icon-cancel') if @content.version < @page.content.version %>
+<% end %>
 <%= link_to(l(:label_history), {:action => 'history', :page => @page.title}, :class => 'icon icon-history') %>
 </div>
 
@@ -22,9 +26,9 @@
 
 <%= render(:partial => "wiki/content", :locals => {:content => @content}) %>
 
-<%= link_to_attachments @page.attachments, :delete_url => (authorize_for('wiki', 'destroy_attachment') ? {:controller => 'wiki', :action => 'destroy_attachment', :page => @page.title} : nil) %>
+<%= link_to_attachments @page.attachments, :delete_url => ((@editable && authorize_for('wiki', 'destroy_attachment')) ? {:controller => 'wiki', :action => 'destroy_attachment', :page => @page.title} : nil) %>
 
-<% if authorize_for('wiki', 'add_attachment') %>
+<% if @editable && authorize_for('wiki', 'add_attachment') %>
 <p><%= link_to l(:label_attachment_new), {}, :onclick => "Element.show('add_attachment_form'); Element.hide(this); Element.scrollTo('add_attachment_form'); return false;",
                                              :id => 'attach_files_link' %></p>
 <% form_tag({ :controller => 'wiki', :action => 'add_attachment', :page => @page.title }, :multipart => true, :id => "add_attachment_form", :style => "display:none;") do %>
diff --git a/db/migrate/093_add_wiki_pages_protected.rb b/db/migrate/093_add_wiki_pages_protected.rb
new file mode 100644 (file)
index 0000000..49720fb
--- /dev/null
@@ -0,0 +1,9 @@
+class AddWikiPagesProtected < ActiveRecord::Migration
+  def self.up
+    add_column :wiki_pages, :protected, :boolean, :default => false, :null => false
+  end
+
+  def self.down
+    remove_column :wiki_pages, :protected
+  end
+end
index cef2615..b21f571 100644 (file)
@@ -76,6 +76,7 @@ Redmine::AccessControl.map do |map|
     map.permission :delete_wiki_pages, {:wiki => :destroy}, :require => :member
     map.permission :view_wiki_pages, :wiki => [:index, :history, :diff, :annotate, :special]
     map.permission :edit_wiki_pages, :wiki => [:edit, :preview, :add_attachment, :destroy_attachment]
+    map.permission :protect_wiki_pages, {:wiki => :protect}, :require => :member
   end
     
   map.project_module :repository do |map|
index c2789e3..82d6299 100644 (file)
Binary files a/public/images/locked.png and b/public/images/locked.png differ
index e0d4149..f15fead 100644 (file)
Binary files a/public/images/unlock.png and b/public/images/unlock.png differ
index 1ede6fc..e7b1422 100644 (file)
@@ -29,6 +29,7 @@ roles_001:
     - :manage_documents\r
     - :view_wiki_pages\r
     - :edit_wiki_pages\r
+    - :protect_wiki_pages\r
     - :delete_wiki_pages\r
     - :rename_wiki_pages\r
     - :add_messages\r
@@ -69,6 +70,7 @@ roles_002:
     - :manage_documents\r
     - :view_wiki_pages\r
     - :edit_wiki_pages\r
+    - :protect_wiki_pages\r
     - :delete_wiki_pages\r
     - :add_messages\r
     - :manage_boards\r
index f89832e..ef72424 100644 (file)
@@ -4,19 +4,23 @@ wiki_pages_001:
   title: CookBook_documentation\r
   id: 1\r
   wiki_id: 1\r
+  protected: true  \r
 wiki_pages_002: \r
   created_on: 2007-03-08 00:18:07 +01:00\r
   title: Another_page\r
   id: 2\r
   wiki_id: 1\r
+  protected: false\r
 wiki_pages_003: \r
   created_on: 2007-03-08 00:18:07 +01:00\r
   title: Start_page\r
   id: 3\r
   wiki_id: 2\r
+  protected: false\r
 wiki_pages_004: \r
   created_on: 2007-03-08 00:18:07 +01:00\r
   title: Page_with_an_inline_image\r
   id: 4\r
   wiki_id: 1\r
+  protected: false\r
   
\ No newline at end of file
index bf31e66..8688c2e 100644 (file)
@@ -160,4 +160,60 @@ class WikiControllerTest < Test::Unit::TestCase
     get :index, :id => 999
     assert_response 404
   end
+  
+  def test_protect_page
+    page = WikiPage.find_by_wiki_id_and_title(1, 'Another_page')
+    assert !page.protected?
+    @request.session[:user_id] = 2
+    post :protect, :id => 1, :page => page.title, :protected => '1'
+    assert_redirected_to 'wiki/ecookbook/Another_page'
+    assert page.reload.protected?
+  end
+  
+  def test_unprotect_page
+    page = WikiPage.find_by_wiki_id_and_title(1, 'CookBook_documentation')
+    assert page.protected?
+    @request.session[:user_id] = 2
+    post :protect, :id => 1, :page => page.title, :protected => '0'
+    assert_redirected_to 'wiki/ecookbook'
+    assert !page.reload.protected?
+  end
+  
+  def test_show_page_with_edit_link
+    @request.session[:user_id] = 2
+    get :index, :id => 1
+    assert_response :success
+    assert_template 'show'
+    assert_tag :tag => 'a', :attributes => { :href => '/wiki/1/CookBook_documentation/edit' }
+  end
+  
+  def test_show_page_without_edit_link
+    @request.session[:user_id] = 4
+    get :index, :id => 1
+    assert_response :success
+    assert_template 'show'
+    assert_no_tag :tag => 'a', :attributes => { :href => '/wiki/1/CookBook_documentation/edit' }
+  end  
+  
+  def test_edit_unprotected_page
+    # Non members can edit unprotected wiki pages
+    @request.session[:user_id] = 4
+    get :edit, :id => 1, :page => 'Another_page'
+    assert_response :success
+    assert_template 'edit'
+  end
+  
+  def test_edit_protected_page_by_nonmember
+    # Non members can't edit protected wiki pages
+    @request.session[:user_id] = 4
+    get :edit, :id => 1, :page => 'CookBook_documentation'
+    assert_response 403
+  end
+  
+  def test_edit_protected_page_by_member
+    @request.session[:user_id] = 2
+    get :edit, :id => 1, :page => 'CookBook_documentation'
+    assert_response :success
+    assert_template 'edit'    
+  end
 end