Defect #43360
openRestApi modify/add wiki page results in broken wiki state
0%
Description
I know that the rest api for wikis is in alpha but breaking redmine is not what I was expecting.
I need to modify a lot external hyperlinks on many wiki pages so I went to the Rest API to modify the page.
I am using the rest api:- project api to fetch all the projects which has wiki module enabled in json.
- all the wiki page names of the project
- each wiki-page content to see if I need to modify it.
But when I want to modify the page via XML but also tested JSON, it is returning status code 201 Created
not 204 No Content
.
Below a XML example used to PUT to https://redmine.internal.domain/projects/prj/wiki/Release.xml
, version of the wikipage is 5.
<?xml version="1.0"?><wiki_page><text>Some test</text><version>5</version><comments>Update links to SVN see #11655</comments></wiki_page>
In redmine the page in question is not modify.
If I click on the wiki -> index by date
, the page returns 500 Internal error
. See also log output below.
But index by name
I can see the pagename when using a other Release-test.xml
.
logs snippits¶
Redmine log when clicking of index by date
I, [2025-10-16T14:26:15.911151 #1] INFO -- : [e4e0d7c8-1537-4d2d-99ff-904b2cf39f31] Started GET "/projects/prj/wiki/date_index" for <ipadr> at 2025-10-16 14:26:15 +0200 I, [2025-10-16T14:26:15.911810 #1] INFO -- : [e4e0d7c8-1537-4d2d-99ff-904b2cf39f31] Processing by WikiController#date_index as HTML I, [2025-10-16T14:26:15.911841 #1] INFO -- : [e4e0d7c8-1537-4d2d-99ff-904b2cf39f31] Parameters: {"project_id"=>"prj"} I, [2025-10-16T14:26:15.917863 #1] INFO -- : [e4e0d7c8-1537-4d2d-99ff-904b2cf39f31] Current user: r.v.dorst (id=7) I, [2025-10-16T14:26:15.923989 #1] INFO -- : [e4e0d7c8-1537-4d2d-99ff-904b2cf39f31] Completed 500 Internal Server Error in 12ms (ActiveRecord: 4.9ms (13 queries, 1 cached) | GC: 0.0ms) F, [2025-10-16T14:26:15.927093 #1] FATAL -- : [e4e0d7c8-1537-4d2d-99ff-904b2cf39f31] [e4e0d7c8-1537-4d2d-99ff-904b2cf39f31] NoMethodError (undefined method `to_date' for nil): [e4e0d7c8-1537-4d2d-99ff-904b2cf39f31] [e4e0d7c8-1537-4d2d-99ff-904b2cf39f31] app/controllers/wiki_controller.rb:62:in `block in date_index' [e4e0d7c8-1537-4d2d-99ff-904b2cf39f31] app/controllers/wiki_controller.rb:62:in `each' [e4e0d7c8-1537-4d2d-99ff-904b2cf39f31] app/controllers/wiki_controller.rb:62:in `group_by' [e4e0d7c8-1537-4d2d-99ff-904b2cf39f31] app/controllers/wiki_controller.rb:62:in `date_index' [e4e0d7c8-1537-4d2d-99ff-904b2cf39f31] lib/redmine/sudo_mode.rb:78:in `sudo_mode'
Redmine Log for the XML PUT
I, [2025-10-16T14:25:33.264844 #1] INFO -- : [9f9f6af1-64cf-4956-b3e7-1b4f73d5792d] Started PUT "/projects/prj/wiki/Release.xml?key=<snip>" for <ipadr> at 2025-10-16 14:25:33 +0200 I, [2025-10-16T14:25:33.265305 #1] INFO -- : [9f9f6af1-64cf-4956-b3e7-1b4f73d5792d] Processing by WikiController#update as XML I, [2025-10-16T14:25:33.265329 #1] INFO -- : [9f9f6af1-64cf-4956-b3e7-1b4f73d5792d] Parameters: {"key"=>"<snip>", "project_id"=>"prj", "id"=>"Release"} I, [2025-10-16T14:25:33.267488 #1] INFO -- : [9f9f6af1-64cf-4956-b3e7-1b4f73d5792d] Current user: r.v.dorst (id=7) I, [2025-10-16T14:25:33.277452 #1] INFO -- : [9f9f6af1-64cf-4956-b3e7-1b4f73d5792d] Completed 201 Created in 12ms (Views: 0.9ms | ActiveRecord: 4.8ms (14 queries, 2 cached) | GC: 0.0ms)
database¶
After some investigation in the database.
I see:
MariaDB [redmine]> select * from wiki_pages order by id desc limit 10; +------+---------+-----------------------------+---------------------+-----------+-----------+ | id | wiki_id | title | created_on | protected | parent_id | +------+---------+-----------------------------+---------------------+-----------+-----------+ | 2697 | 160 | Index | 2025-10-16 14:25:33 | 0 | NULL |
MariaDB [redmine]> select * from wiki_contents where page_id = 2697 ; Empty set (0.001 sec)
rerun the script¶
When running the code again. Fetching the wikipages json goes wrong to.
See the last line, version
=null
and updated_on
=null
.
{"wiki_pages":[{"title":"Changelog","parent":{"title":"Wiki"},"version":3,"created_on":"2021-04-20T12:51:32Z","updated_on":"2022-04-04T10:25:09Z"},{"title":"Deleted","version":6,"created_on":"2016-10-05T09:21:01Z","updated_on":"2021-04-22T15:46:54Z"}, {"title":"Index","version":null,"created_on":"2025-10-16T12:12:32Z","updated_on":null},
fix the issue¶
To fix the index by date
issue and JSON API, removing id=2697
from wiki_pages
seems enough to fix it.
I hope this can be fix soon or have a workaround.
Updated by René van Dorst 3 days ago
Update, When I remove the version
field from the request
<?xml version="1.0"?><wiki_page><text>Some test</text><comments>Update links to SVN see #11655</comments></wiki_page>
Redmine returns error422 Unprocessable
<errors type="array"><error>Text field cannot be blank</error></errors>
for me text field seems filled so looks like the input parser is maybe faulty?
Updated by Holger Just 3 days ago
- Category changed from REST API to Wiki
- Status changed from New to Confirmed
When sending XML or JSON to the API, you have to make sure to set the Content-Type
header of your request accordingly. The .xml
or .json
extension on the URL path only determines the format of the returned data, not the way the data from the request is [arsed. It is entirely possible to send JSON (or browser form data) and receive XML back. Specifically the XML data from your first example, can be successfully parses as form data (which is the default if no Content-Type
is set in the request). However, as this does not extract the actual fields from the XML, it is basically an empty request. Once you set the appropriate Content-Type
header in your request (application/xml
in this case), your API calls should succeed.
Still, it seems you have create a wiki page without any content. It seems that Redmine currently does not fully handle this case everywhere (which has resulted in the exception you saw). The exception in showing the date index with content-less pages in Redmine could be solved with the following patch:
diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb
index bcb3b08911..967e1eaf0f 100644
--- a/app/controllers/wiki_controller.rb
+++ b/app/controllers/wiki_controller.rb
@@ -59,7 +59,7 @@ def index
# 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}
+ @pages_by_date = @pages.group_by {|p| p.updated_on&.to_date}
end
def new
diff --git a/app/views/wiki/date_index.html.erb b/app/views/wiki/date_index.html.erb
index c8acf933c9..24fec82a1a 100644
--- a/app/views/wiki/date_index.html.erb
+++ b/app/views/wiki/date_index.html.erb
@@ -14,7 +14,7 @@
<p class="nodata"><%= l(:label_no_data) %></p>
<% end %>
-<% @pages_by_date.keys.sort.reverse_each do |date| %>
+<% @pages_by_date.keys.sort_by { |date| date || Float::INFINITY }.reverse_each do |date| %>
<h3><%= format_date(date) %></h3>
<ul>
<% @pages_by_date[date].each do |page| %>
diff --git a/test/functional/wiki_controller_test.rb b/test/functional/wiki_controller_test.rb
index 04b687531c..4e5196dca7 100644
--- a/test/functional/wiki_controller_test.rb
+++ b/test/functional/wiki_controller_test.rb
@@ -340,6 +340,23 @@ def test_create_page
assert_equal 'Created the page', page.content.comments
end
+ def test_create_empty_page
+ @request.session[:user_id] = 2
+ assert_difference 'WikiPage.count' do
+ assert_no_difference 'WikiContent.count' do
+ put :update, :params => {
+ :project_id => 1,
+ :id => 'New page'
+ }
+ end
+ end
+ assert_redirected_to :action => 'show', :project_id => 'ecookbook', :id => 'New_page'
+ page = Project.find(1).wiki.find_page('New page')
+ assert !page.new_record?
+ assert_nil page.content
+ assert_nil page.parent
+ end
+
def test_create_page_with_attachments
set_tmp_attachments_directory
@request.session[:user_id] = 2
@@ -1129,6 +1146,15 @@ def test_date_index
assert_select 'a[href=?]', '/projects/ecookbook/activity.atom?show_wiki_edits=1'
end
+ def test_date_index_with_empty_page
+ # Create an empty page with no content
+ WikiPage.create!(:wiki_id => 1, :title => 'Foo')
+
+ get :date_index, :params => {:project_id => 'ecookbook'}
+ assert_response :success
+ assert_select 'a[href=?]', '/projects/ecookbook/wiki/Foo'
+ end
+
def test_not_found
get :show, :params => {:project_id => 999}
assert_response :not_found
We may also want to adjust the validation logic so that it is not possible to create a new WikiPage
without an associated WikiPageContent
in the first place. Accordingly, we may then also want to destroy a wiki page entirely when its last WikiPageContent
is destroyed.
Updated by René van Dorst 3 days ago
Holger, thanks for your quick feedback.
Your extra explanation is great feedback and useful to know.
I made an big error.
I did not send the body, XML part, in the request.
I also did not send the content-type.
Strange to see that I go different result when changing the "XML part".
Probably to do with using a existing wiki-page vs non-existing name while testing.
As you explained that it still possible to make a new wiki page without content, which I probably did while testing.
Sending a requenst with body and content-type, everything is working as expected.
I still going to test your patch, need some time to setup a dev environment.