##// END OF EJS Templates
REST API for creating/updating wiki pages (#7082)....
Jean-Philippe Lang -
r10505:eff874b29a90
parent child
Show More
@@ -553,8 +553,13 class ApplicationController < ActionController::Base
553
553
554 # Renders a 200 response for successfull updates or deletions via the API
554 # Renders a 200 response for successfull updates or deletions via the API
555 def render_api_ok
555 def render_api_ok
556 # head :ok would return a response body with one space
556 render_api_head :ok
557 render :text => '', :status => :ok, :layout => nil
557 end
558
559 # Renders a head API response
560 def render_api_head(status)
561 # #head would return a response body with one space
562 render :text => '', :status => status, :layout => nil
558 end
563 end
559
564
560 # Renders API response on validation failure
565 # Renders API response on validation failure
@@ -36,7 +36,7 class WikiController < ApplicationController
36 before_filter :find_wiki, :authorize
36 before_filter :find_wiki, :authorize
37 before_filter :find_existing_or_new_page, :only => [:show, :edit, :update]
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]
38 before_filter :find_existing_page, :only => [:rename, :protect, :history, :diff, :annotate, :add_attachment, :destroy, :destroy_version]
39 accept_api_auth :index, :show
39 accept_api_auth :index, :show, :update
40
40
41 helper :attachments
41 helper :attachments
42 include AttachmentsHelper
42 include AttachmentsHelper
@@ -130,15 +130,18 class WikiController < ApplicationController
130 # Creates a new page or updates an existing one
130 # Creates a new page or updates an existing one
131 def update
131 def update
132 return render_403 unless editable?
132 return render_403 unless editable?
133 was_new_page = @page.new_record?
133 @page.content = WikiContent.new(:page => @page) if @page.new_record?
134 @page.content = WikiContent.new(:page => @page) if @page.new_record?
134 @page.safe_attributes = params[:wiki_page]
135 @page.safe_attributes = params[:wiki_page]
135
136
136 @content = @page.content_for_version(params[:version])
137 @content = @page.content
137 @content.text = initial_page_content(@page) if @content.text.blank?
138 content_params = params[:content]
138 # don't keep previous comment
139 if content_params.nil? && params[:wiki_page].is_a?(Hash)
139 @content.comments = nil
140 content_params = params[:wiki_page].slice(:text, :comments, :version)
141 end
142 content_params ||= {}
140
143
141 if !@page.new_record? && params[:content].present? && @content.text == params[:content][:text]
144 if !@page.new_record? && content_params.present? && @content.text == content_params[:text]
142 attachments = Attachment.attach_files(@page, params[:attachments])
145 attachments = Attachment.attach_files(@page, params[:attachments])
143 render_attachment_warning_if_needed(@page)
146 render_attachment_warning_if_needed(@page)
144 # don't save content if text wasn't changed
147 # don't save content if text wasn't changed
@@ -147,14 +150,14 class WikiController < ApplicationController
147 return
150 return
148 end
151 end
149
152
150 @content.comments = params[:content][:comments]
153 @content.comments = content_params[:comments]
151 @text = params[:content][:text]
154 @text = content_params[:text]
152 if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
155 if params[:section].present? && Redmine::WikiFormatting.supports_section_edit?
153 @section = params[:section].to_i
156 @section = params[:section].to_i
154 @section_hash = params[:section_hash]
157 @section_hash = params[:section_hash]
155 @content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(params[:section].to_i, @text, @section_hash)
158 @content.text = Redmine::WikiFormatting.formatter.new(@content.text).update_section(params[:section].to_i, @text, @section_hash)
156 else
159 else
157 @content.version = params[:content][:version]
160 @content.version = content_params[:version] if content_params[:version]
158 @content.text = @text
161 @content.text = @text
159 end
162 end
160 @content.author = User.current
163 @content.author = User.current
@@ -163,17 +166,38 class WikiController < ApplicationController
163 attachments = Attachment.attach_files(@page, params[:attachments])
166 attachments = Attachment.attach_files(@page, params[:attachments])
164 render_attachment_warning_if_needed(@page)
167 render_attachment_warning_if_needed(@page)
165 call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
168 call_hook(:controller_wiki_edit_after_save, { :params => params, :page => @page})
166 redirect_to :action => 'show', :project_id => @project, :id => @page.title
169
170 respond_to do |format|
171 format.html { redirect_to :action => 'show', :project_id => @project, :id => @page.title }
172 format.api {
173 if was_new_page
174 render :action => 'show', :status => :created, :location => url_for(:controller => 'wiki', :action => 'show', :project_id => @project, :id => @page.title)
175 else
176 render_api_ok
177 end
178 }
179 end
167 else
180 else
168 render :action => 'edit'
181 respond_to do |format|
182 format.html { render :action => 'edit' }
183 format.api { render_validation_errors(@content) }
184 end
169 end
185 end
170
186
171 rescue ActiveRecord::StaleObjectError, Redmine::WikiFormatting::StaleSectionError
187 rescue ActiveRecord::StaleObjectError, Redmine::WikiFormatting::StaleSectionError
172 # Optimistic locking exception
188 # Optimistic locking exception
173 flash.now[:error] = l(:notice_locking_conflict)
189 respond_to do |format|
174 render :action => 'edit'
190 format.html {
191 flash.now[:error] = l(:notice_locking_conflict)
192 render :action => 'edit'
193 }
194 format.api { render_api_head :conflict }
195 end
175 rescue ActiveRecord::RecordNotSaved
196 rescue ActiveRecord::RecordNotSaved
176 render :action => 'edit'
197 respond_to do |format|
198 format.html { render :action => 'edit' }
199 format.api { render_validation_errors(@content) }
200 end
177 end
201 end
178
202
179 # rename a page
203 # rename a page
@@ -57,7 +57,7 class WikiPage < ActiveRecord::Base
57 # Wiki pages that are protected by default
57 # Wiki pages that are protected by default
58 DEFAULT_PROTECTED_PAGES = %w(sidebar)
58 DEFAULT_PROTECTED_PAGES = %w(sidebar)
59
59
60 safe_attributes 'parent_id',
60 safe_attributes 'parent_id', 'parent_title',
61 :if => lambda {|page, user| page.new_record? || user.allowed_to?(:rename_wiki_pages, page.project)}
61 :if => lambda {|page, user| page.new_record? || user.allowed_to?(:rename_wiki_pages, page.project)}
62
62
63 def initialize(attributes=nil, *args)
63 def initialize(attributes=nil, *args)
@@ -28,7 +28,7 class ApiTest::WikiPagesTest < ActionController::IntegrationTest
28
28
29 test "GET /projects/:project_id/wiki/index.xml should return wiki pages" do
29 test "GET /projects/:project_id/wiki/index.xml should return wiki pages" do
30 get '/projects/ecookbook/wiki/index.xml'
30 get '/projects/ecookbook/wiki/index.xml'
31 assert_response :success
31 assert_response 200
32 assert_equal 'application/xml', response.content_type
32 assert_equal 'application/xml', response.content_type
33 assert_select 'wiki_pages[type=array]' do
33 assert_select 'wiki_pages[type=array]' do
34 assert_select 'wiki_page', :count => Wiki.find(1).pages.count
34 assert_select 'wiki_page', :count => Wiki.find(1).pages.count
@@ -38,12 +38,16 class ApiTest::WikiPagesTest < ActionController::IntegrationTest
38 assert_select 'created_on'
38 assert_select 'created_on'
39 assert_select 'updated_on'
39 assert_select 'updated_on'
40 end
40 end
41 assert_select 'wiki_page' do
42 assert_select 'title', :text => 'Page_with_an_inline_image'
43 assert_select 'parent[title=?]', 'CookBook_documentation'
44 end
41 end
45 end
42 end
46 end
43
47
44 test "GET /projects/:project_id/wiki/:title.xml should return wiki page" do
48 test "GET /projects/:project_id/wiki/:title.xml should return wiki page" do
45 get '/projects/ecookbook/wiki/CookBook_documentation.xml'
49 get '/projects/ecookbook/wiki/CookBook_documentation.xml'
46 assert_response :success
50 assert_response 200
47 assert_equal 'application/xml', response.content_type
51 assert_equal 'application/xml', response.content_type
48 assert_select 'wiki_page' do
52 assert_select 'wiki_page' do
49 assert_select 'title', :text => 'CookBook_documentation'
53 assert_select 'title', :text => 'CookBook_documentation'
@@ -63,7 +67,7 class ApiTest::WikiPagesTest < ActionController::IntegrationTest
63
67
64 test "GET /projects/:project_id/wiki/:title/:version.xml should return wiki page version" do
68 test "GET /projects/:project_id/wiki/:title/:version.xml should return wiki page version" do
65 get '/projects/ecookbook/wiki/CookBook_documentation/2.xml'
69 get '/projects/ecookbook/wiki/CookBook_documentation/2.xml'
66 assert_response :success
70 assert_response 200
67 assert_equal 'application/xml', response.content_type
71 assert_equal 'application/xml', response.content_type
68 assert_select 'wiki_page' do
72 assert_select 'wiki_page' do
69 assert_select 'title', :text => 'CookBook_documentation'
73 assert_select 'title', :text => 'CookBook_documentation'
@@ -82,4 +86,83 class ApiTest::WikiPagesTest < ActionController::IntegrationTest
82 assert_response 401
86 assert_response 401
83 assert_equal 'application/xml', response.content_type
87 assert_equal 'application/xml', response.content_type
84 end
88 end
89
90 test "PUT /projects/:project_id/wiki/:title.xml should update wiki page" do
91 assert_no_difference 'WikiPage.count' do
92 assert_difference 'WikiContent::Version.count' do
93 put '/projects/ecookbook/wiki/CookBook_documentation.xml',
94 {:wiki_page => {:text => 'New content from API', :comments => 'API update'}},
95 credentials('jsmith')
96 assert_response 200
97 end
98 end
99
100 page = WikiPage.find(1)
101 assert_equal 'New content from API', page.content.text
102 assert_equal 4, page.content.version
103 assert_equal 'API update', page.content.comments
104 assert_equal 'jsmith', page.content.author.login
105 end
106
107 test "PUT /projects/:project_id/wiki/:title.xml with current versino should update wiki page" do
108 assert_no_difference 'WikiPage.count' do
109 assert_difference 'WikiContent::Version.count' do
110 put '/projects/ecookbook/wiki/CookBook_documentation.xml',
111 {:wiki_page => {:text => 'New content from API', :comments => 'API update', :version => '3'}},
112 credentials('jsmith')
113 assert_response 200
114 end
115 end
116
117 page = WikiPage.find(1)
118 assert_equal 'New content from API', page.content.text
119 assert_equal 4, page.content.version
120 assert_equal 'API update', page.content.comments
121 assert_equal 'jsmith', page.content.author.login
122 end
123
124 test "PUT /projects/:project_id/wiki/:title.xml with stale version should respond with 409" do
125 assert_no_difference 'WikiPage.count' do
126 assert_no_difference 'WikiContent::Version.count' do
127 put '/projects/ecookbook/wiki/CookBook_documentation.xml',
128 {:wiki_page => {:text => 'New content from API', :comments => 'API update', :version => '2'}},
129 credentials('jsmith')
130 assert_response 409
131 end
132 end
133 end
134
135 test "PUT /projects/:project_id/wiki/:title.xml should create the page if it does not exist" do
136 assert_difference 'WikiPage.count' do
137 assert_difference 'WikiContent::Version.count' do
138 put '/projects/ecookbook/wiki/New_page_from_API.xml',
139 {:wiki_page => {:text => 'New content from API', :comments => 'API create'}},
140 credentials('jsmith')
141 assert_response 201
142 end
143 end
144
145 page = WikiPage.order('id DESC').first
146 assert_equal 'New_page_from_API', page.title
147 assert_equal 'New content from API', page.content.text
148 assert_equal 1, page.content.version
149 assert_equal 'API create', page.content.comments
150 assert_equal 'jsmith', page.content.author.login
151 assert_nil page.parent
152 end
153
154 test "PUT /projects/:project_id/wiki/:title.xml with parent" do
155 assert_difference 'WikiPage.count' do
156 assert_difference 'WikiContent::Version.count' do
157 put '/projects/ecookbook/wiki/New_subpage_from_API.xml',
158 {:wiki_page => {:parent_title => 'CookBook_documentation', :text => 'New content from API', :comments => 'API create'}},
159 credentials('jsmith')
160 assert_response 201
161 end
162 end
163
164 page = WikiPage.order('id DESC').first
165 assert_equal 'New_subpage_from_API', page.title
166 assert_equal WikiPage.find(1), page.parent
167 end
85 end
168 end
@@ -34,16 +34,6 class RoutingWikiTest < ActionController::IntegrationTest
34 :id => 'lalala', :format => 'pdf' }
34 :id => 'lalala', :format => 'pdf' }
35 )
35 )
36 assert_routing(
36 assert_routing(
37 { :method => 'get', :path => "/projects/567/wiki/lalala.xml" },
38 { :controller => 'wiki', :action => 'show', :project_id => '567',
39 :id => 'lalala', :format => 'xml' }
40 )
41 assert_routing(
42 { :method => 'get', :path => "/projects/567/wiki/lalala.json" },
43 { :controller => 'wiki', :action => 'show', :project_id => '567',
44 :id => 'lalala', :format => 'json' }
45 )
46 assert_routing(
47 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/diff" },
37 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/diff" },
48 { :controller => 'wiki', :action => 'diff', :project_id => '1',
38 { :controller => 'wiki', :action => 'diff', :project_id => '1',
49 :id => 'CookBook_documentation' }
39 :id => 'CookBook_documentation' }
@@ -54,16 +44,6 class RoutingWikiTest < ActionController::IntegrationTest
54 :id => 'CookBook_documentation', :version => '2' }
44 :id => 'CookBook_documentation', :version => '2' }
55 )
45 )
56 assert_routing(
46 assert_routing(
57 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/2.xml" },
58 { :controller => 'wiki', :action => 'show', :project_id => '1',
59 :id => 'CookBook_documentation', :version => '2', :format => 'xml' }
60 )
61 assert_routing(
62 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/2.json" },
63 { :controller => 'wiki', :action => 'show', :project_id => '1',
64 :id => 'CookBook_documentation', :version => '2', :format => 'json' }
65 )
66 assert_routing(
67 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/2/diff" },
47 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/2/diff" },
68 { :controller => 'wiki', :action => 'diff', :project_id => '1',
48 { :controller => 'wiki', :action => 'diff', :project_id => '1',
69 :id => 'CookBook_documentation', :version => '2' }
49 :id => 'CookBook_documentation', :version => '2' }
@@ -92,14 +72,6 class RoutingWikiTest < ActionController::IntegrationTest
92 { :method => 'get', :path => "/projects/567/wiki/index" },
72 { :method => 'get', :path => "/projects/567/wiki/index" },
93 { :controller => 'wiki', :action => 'index', :project_id => '567' }
73 { :controller => 'wiki', :action => 'index', :project_id => '567' }
94 )
74 )
95 assert_routing(
96 { :method => 'get', :path => "/projects/567/wiki/index.xml" },
97 { :controller => 'wiki', :action => 'index', :project_id => '567', :format => 'xml' }
98 )
99 assert_routing(
100 { :method => 'get', :path => "/projects/567/wiki/index.json" },
101 { :controller => 'wiki', :action => 'index', :project_id => '567', :format => 'json' }
102 )
103 end
75 end
104
76
105 def test_wiki_resources
77 def test_wiki_resources
@@ -156,4 +128,45 class RoutingWikiTest < ActionController::IntegrationTest
156 :id => 'ladida', :version => '3' }
128 :id => 'ladida', :version => '3' }
157 )
129 )
158 end
130 end
131
132 def test_api
133 assert_routing(
134 { :method => 'get', :path => "/projects/567/wiki/my_page.xml" },
135 { :controller => 'wiki', :action => 'show', :project_id => '567',
136 :id => 'my_page', :format => 'xml' }
137 )
138 assert_routing(
139 { :method => 'get', :path => "/projects/567/wiki/my_page.json" },
140 { :controller => 'wiki', :action => 'show', :project_id => '567',
141 :id => 'my_page', :format => 'json' }
142 )
143 assert_routing(
144 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/2.xml" },
145 { :controller => 'wiki', :action => 'show', :project_id => '1',
146 :id => 'CookBook_documentation', :version => '2', :format => 'xml' }
147 )
148 assert_routing(
149 { :method => 'get', :path => "/projects/1/wiki/CookBook_documentation/2.json" },
150 { :controller => 'wiki', :action => 'show', :project_id => '1',
151 :id => 'CookBook_documentation', :version => '2', :format => 'json' }
152 )
153 assert_routing(
154 { :method => 'get', :path => "/projects/567/wiki/index.xml" },
155 { :controller => 'wiki', :action => 'index', :project_id => '567', :format => 'xml' }
156 )
157 assert_routing(
158 { :method => 'get', :path => "/projects/567/wiki/index.json" },
159 { :controller => 'wiki', :action => 'index', :project_id => '567', :format => 'json' }
160 )
161 assert_routing(
162 { :method => 'put', :path => "/projects/567/wiki/my_page.xml" },
163 { :controller => 'wiki', :action => 'update', :project_id => '567',
164 :id => 'my_page', :format => 'xml' }
165 )
166 assert_routing(
167 { :method => 'put', :path => "/projects/567/wiki/my_page.json" },
168 { :controller => 'wiki', :action => 'update', :project_id => '567',
169 :id => 'my_page', :format => 'json' }
170 )
171 end
159 end
172 end
General Comments 0
You need to be logged in to leave comments. Login now