User tests: Successful: Unsuccessful:
Pull Request resolves #48444 .
PATCH /api/index.php/v1/menus/site/items/{id} returns a 500 (Error loading form file) for any existing item unless the body repeats "type".
Cause. ItemModel::populateState() takes the item type from the user state (com_menus.edit.item.type) or, if that is empty, from the request input. The user state is only populated by the backend edit screen, so for a stateless API request the type is only known if the caller sent it. With no type, item.type is empty, preprocessForm() builds item_ as the form name, Path::find() finds nothing and Form::loadFile() throws JERROR_LOADFILE_FAILED. The request has already checked the item out by then, so a failed call also leaves it locked.
Change. In populateState(), when no type was found and an existing item was loaded earlier in the method ($pk set, so $table is loaded), use that row's type:
if (!$type && isset($table)) {
$type = $table->type;
}$table is not set when there is no id, so behaviour is unchanged.type sent in the request still wins over the stored one, so changing an item's type over the API works as before.Workaround on current releases (no patch needed). Repeat the item's existing type at the top level of the body:
{ "type": "component", "params": { "pageclass_sfx": "my-page-class" } }This returns 200 and the change persists (checked with a separate GET). Confirmed on 5.4.8 in the issue.
AI disclosure: the analysis, the patch and this description were produced with Claude (Anthropic) in a working session with me. I reproduced and tested the change as described below before submitting.
Joomla 6.1 with the Web Services plugins and "API Authentication - Token" enabled, and an API token for a Super User.
PATCH any existing site menu item without type:
curl -i -X PATCH \
-H "X-Joomla-Token: <token>" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.api+json" \
-d '{"params":{"pageclass_sfx":"my-page-class"}}' \
"https://example.com/api/index.php/v1/menus/site/items/101"
GET the same URL and read attributes.params.pageclass_sfx.
Repeat step 1 with "type":"component" added to the body.
Create a new item with POST (with type) to check creation still works.
Step 1: 500 Internal server error; with debug on, Exception: Error loading form file from ItemModel::preprocessForm(). The item stays checked out by the API user (checked_out is set).
Step 1: 200 OK. Step 2: pageclass_sfx is saved and type is unchanged. The item is not left checked out. Step 3: 200 OK.
Tested on Joomla 6.1.4 over the API: before the change, step 1 returned 500 and left the item checked out; after the change it returned 200, the value persisted and checked_out was empty; with the change reverted it returned 500 again. Step 3 returned 200 with the change applied. PATCH on an unknown id returns 400 ("That item is no longer available"). Not run: the Joomla automated test suite, and the backend edit screen (its code path is unchanged).
Please select:
Documentation link for guide.joomla.org:
No documentation changes for guide.joomla.org needed
Pull Request link for manual.joomla.org:
No documentation changes for manual.joomla.org needed
| Status | New | ⇒ | Pending |
| Category | ⇒ | Administration com_menus |
Thanks for testing @alikon. I repeated your request (PATCH items/101, full params, no type) with the change applied on fresh Joomla 5.4.10, 6.1.3 and 6.2.0 installs, and pageclass_sfx saved each time: it showed in the PATCH response, in a separate GET, and in the #__menu row, with the item left checked in.
Could you tell me which version or branch you applied it to, and what a separate GET on items/101 shows after the PATCH? It would also help to know whether a PATCH of just {"title": "..."} changes the title, whether item 101 is checked out to another user, and what params holds in the database row for that item. I'd like to see what differs from my test sites.
Thanks for testing @alikon. I repeated your request (PATCH items/101, full params, no type) with the change applied on fresh Joomla 5.4.10, 6.1.3 and 6.2.0 installs, and pageclass_sfx saved each time: it showed in the PATCH response, in a separate GET, and in the #__menu row, with the item left checked in.
Could you tell me which version or branch you applied it to, and what a separate GET on items/101 shows after the PATCH? It would also help to know whether a PATCH of just {"title": "..."} changes the title, whether item 101 is checked out to another user, and what params holds in the database row for that item. I'd like to see what differs from my test sites.
Files used for successful test
Thanks for testing @alikon. I repeated your request (PATCH items/101, full params, no type) with the change applied on fresh Joomla 5.4.10, 6.1.3 and 6.2.0 installs, and pageclass_sfx saved each time: it showed in the PATCH response, in a separate GET, and in the #__menu row, with the item left checked in.
Could you tell me which version or branch you applied it to, and what a separate GET on items/101 shows after the PATCH? It would also help to know whether a PATCH of just {"title": "..."} changes the title, whether item 101 is checked out to another user, and what params holds in the database row for that item. I'd like to see what differs from my test sites.
| Title |
|
||||||
| Labels |
Added:
Webservices
PR-6.2-dev
|
||
rebased to 6.2-dev
i've redonew the test on a fresh 6.2 after the rebase of the pr, still same result as before
curl --request PATCH \
--url http://localhost:7062/api/index.php/v1/menus/site/items/101 \
--header 'Accept: application/vnd.api+json' \
--header 'Content-Type: application/vnd.api+json' \
--header 'X-Joomla-Token: c2hhMjU2OjI5NTphYWRiYmYzYjVlMjczZmQyNmVkY2VjZWM2YzQ0ZTYzMDFkMWFmYmU2N2MyZmVkNGI5ZTdmNGFjMmVmNDE0OWY2' \
--data '"params": {
"featured_categories": [
""
],
"layout_type": "blog",
"blog_class_leading": "",
"blog_class": "",
"num_leading_articles": "1",
"num_intro_articles": "3",
"num_links": "0",
"link_intro_image": "",
"orderby_pri": "",
"orderby_sec": "front",
"order_date": "",
"show_pagination": "2",
"show_pagination_results": "1",
"show_title": "",
"link_titles": "",
"show_intro": "",
"info_block_position": "",
"info_block_show_title": "",
"show_category": "",
"link_category": "",
"show_parent_category": "",
"link_parent_category": "",
"show_associations": "",
"show_author": "",
"link_author": "",
"show_create_date": "",
"show_modify_date": "",
"show_publish_date": "",
"show_item_navigation": "",
"show_vote": "",
"show_readmore": "",
"show_readmore_title": "",
"show_hits": "",
"show_tags": "",
"show_noauth": "",
"show_feed_link": "1",
"feed_summary": "",
"menu-anchor_title": "",
"menu-anchor_css": "",
"menu_image": "",
"menu_image_css": "",
"menu_text": 1,
"menu_show": 1,
"page_title": "",
"show_page_heading": "1",
"page_heading": "",
"pageclass_sfx": "test",
"menu-meta_description": "",
"robots": ""
}'this is the response
{
"links": {
"self": "http://localhost:7062/api/index.php/v1/menus/site/items/101"
},
"data": {
"type": "items",
"id": "101",
"attributes": {
"typeAlias": null,
"parent_id": 1,
"level": 1,
"lft": 41,
"rgt": 42,
"alias": "home",
"id": 101,
"menutype": "mainmenu",
"title": "Home",
"note": "",
"path": "home",
"link": "index.php?option=com_content&view=featured",
"type": "component",
"published": 1,
"component_id": 19,
"checked_out": null,
"checked_out_time": null,
"browserNav": 0,
"access": 1,
"img": " ",
"template_style_id": 0,
"params": {
"featured_categories": [
""
],
"layout_type": "blog",
"blog_class_leading": "",
"blog_class": "",
"num_leading_articles": "1",
"num_intro_articles": "3",
"num_links": "0",
"link_intro_image": "",
"orderby_pri": "",
"orderby_sec": "front",
"order_date": "",
"show_pagination": "2",
"show_pagination_results": "1",
"show_title": "",
"link_titles": "",
"show_intro": "",
"info_block_position": "",
"info_block_show_title": "",
"show_category": "",
"link_category": "",
"show_parent_category": "",
"link_parent_category": "",
"show_associations": "",
"show_author": "",
"link_author": "",
"show_create_date": "",
"show_modify_date": "",
"show_publish_date": "",
"show_item_navigation": "",
"show_vote": "",
"show_readmore": "",
"show_readmore_title": "",
"show_hits": "",
"show_tags": "",
"show_noauth": "",
"show_feed_link": "1",
"feed_summary": "",
"menu-anchor_title": "",
"menu-anchor_css": "",
"menu_image": "",
"menu_image_css": "",
"menu_text": 1,
"menu_show": 1,
"page_title": "",
"show_page_heading": "1",
"page_heading": "",
"pageclass_sfx": "",
"menu-meta_description": "",
"robots": "",
"option": "com_content",
"view": "featured"
},
"home": 1,
"language": "*",
"client_id": 0,
"publish_up": null,
"publish_down": null,
"request": {
"option": "com_content",
"view": "featured"
},
"menuordering": 101
}
}
}even if after i excute a GET "pageclass_sfx": "" didn't change
no more error but the value has not been changed