Webservices PR-6.2-dev Pending

User tests: Successful: Unsuccessful:

avatar dmlwebal
dmlwebal
16 Oct 2026

Pull Request resolves #48444 .

  • I read the Generative AI policy and my contribution is either not created with the help of AI or is compatible with the policy and GNU/GPL 2 or later.

Summary of Changes

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;
}
  • New items: $table is not set when there is no id, so behaviour is unchanged.
  • Backend edit screen: the user state is still read first, so behaviour is unchanged.
  • A 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.

Testing Instructions

Joomla 6.1 with the Web Services plugins and "API Authentication - Token" enabled, and an API token for a Super User.

  1. 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"
    
  2. GET the same URL and read attributes.params.pageclass_sfx.

  3. Repeat step 1 with "type":"component" added to the body.

  4. Create a new item with POST (with type) to check creation still works.

Actual result BEFORE applying this Pull Request

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).

Expected result AFTER applying this Pull Request

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).

Link to documentations

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

avatar dmlwebal dmlwebal - open - 16 Oct 2026
avatar dmlwebal dmlwebal - change - 16 Oct 2026
Status New ⇒ Pending
avatar joomla-cms-bot joomla-cms-bot - change - 16 Oct 2026
Category ⇒ Administration com_menus
avatar alikon
alikon - comment - 16 Oct 2026

no more error but the value has not been changed

image
avatar dmlwebal
dmlwebal - comment - 16 Oct 2026

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.

image
avatar dmlwebal
dmlwebal - comment - 16 Oct 2026

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.

image

Files used for successful test

pr-48646-drop-in.zip

avatar dmlwebal
dmlwebal - comment - 16 Oct 2026

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.

avatar dmlwebal dmlwebal - edited - 16 Oct 2026
avatar dmlwebal dmlwebal - change - 16 Oct 2026
The description was changed
avatar tecpromotion tecpromotion - change - 19 Oct 2026
Title
…
[6.1] [AI] com_menus: use the stored type when editing an existing item over the API
[6.2] [AI] com_menus: use the stored type when editing an existing item over the API
avatar tecpromotion tecpromotion - edited - 19 Oct 2026
avatar tecpromotion tecpromotion - change - 19 Oct 2026
Labels Added: Webservices PR-6.2-dev
avatar tecpromotion
tecpromotion - comment - 19 Oct 2026

rebased to 6.2-dev

avatar alikon
alikon - comment - 19 Oct 2026

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

Add a Comment

Login with GitHub to post a comment