- Affected version
- 2.3.11
XF\ForumType\DiscussionHandler::addTypeConfigToApiResult() builds the API's allowed_thread_types with the array union operator: 'allowed_thread_types' => [$this->getDefaultThreadType($forum)] + $forum->type_config['allowed_thread_types'],+ keeps the left operand's keys and only adds keys the left side doesn't have. type_config['allowed_thread_types'] is a compact, 0-indexed list, so key 0 — the first enabled extra thread type — is always discarded:
PHP:
["discussion"] + ["poll"] => ["discussion"]
["discussion"] + ["poll", "question"] => ["discussion", "question"]
Steps to reproduce:
1. ACP > Nodes > edit a Discussion forum > Allowed thread types: tick Poll only. Save.
2. GET /api/forums/{node_id} (or /api/nodes/) with an API key.
3. type_data.discussion.allowed_thread_types is ["discussion"]. Expected ["discussion", "poll"].
4. Tick Poll and Question instead: the API returns ["discussion", "question"] — whichever type is first in the forum's (drag-sortable) list is the one dropped.
The new-thread form uses
Forum::getCreatableThreadTypes(), which uses array_unshift(). Only the API result takes the union shortcut, so the two disagree about the same forum.Impact: an API client rendering a thread type picker can't tell that a forum accepts polls or questions, and hides options the forum will happily accept. The list can only ever be short, never over-permissive, so POSTs still validate correctly server-side.
Suggested fix — array_merge instead of the union:
PHP:
'allowed_thread_types' => array_merge(
[$this->getDefaultThreadType($forum)],
$forum->type_config['allowed_thread_types']
),
or simply
$this->getCreatableThreadTypes($forum), which is the same list the new-thread form offers and keeps the API and front end in step by construction.