XF 2.2 Basic process of finding elements in a template and adding them to another

MaximilianKohler

Well-known member
I'm trying to add Display results as threads right below Search titles only in the main search box.

Since Display results as threads exists on the Advanced search page, I was thinking that it would be as simple as finding the lines of code in that template and copying them to the main search box template.

I first went to Search templates and input Search to find templates related to Search. But I could not find an instance of Display results as threads.

So I searched Phrases for Display results as threads and Search titles only and found search_titles_only and display_results_as_threads, and then searched Templates again for search_titles_only and display_results_as_threads. I found what appears to be the lines of code for Search titles only:
Code:
        <ul class="inputList">
            <li><xf:textbox type="search" name="keywords" value="{$input.keywords}" autofocus="autofocus" id="{$controlId}" /></li>
            <xf:if is="$canTitleLimit">
                <li>
                    <xf:checkbox standalone="true">
                        <xf:option name="c[title_only]" selected="{$input.c.title_only}">
                            <xf:label>
                                {{ phrase('search_titles_only') }}

                                <xf:if is="$xf.options.enableTagging">
                                    <span tabindex="0" role="button"
                                        data-xf-init="tooltip" data-trigger="hover focus click" title="{{ phrase('tags_will_also_be_searched')|for_attr }}">

                                        <xf:fa icon="far fa-question-circle" class="u-muted u-smaller" />
                                    </span>
                                </xf:if>
                            </xf:label>
                        </xf:option>
                    </xf:checkbox>
                </li>
            </xf:if>
        </ul>
or
Code:
                                <!--[XF:search_menu:above_title_only]-->
                                <div class="menu-row">
                                    <xf:checkbox standalone="true">
                                        <xf:option name="c[title_only]">
                                            <xf:label>
                                                {{ phrase('search_titles_only') }}

                                                <xf:if is="$xf.options.enableTagging">
                                                    <span tabindex="0" role="button"
                                                        data-xf-init="tooltip" data-trigger="hover focus click" title="{{ phrase('tags_will_also_be_searched')|for_attr }}">

                                                        <xf:fa icon="far fa-question-circle" class="u-muted u-smaller" />
                                                    </span>
                                                </xf:if>
                                            </xf:label>
                                        </xf:option>
                                    </xf:checkbox>
                                </div>

But I cannot find the equivalent for Display results as threads.

I've looked through all the Tips and guides for XF 2.x and didn't find any tutorial for this.
 
I guess we have to resort to asking AI. ChatGPT says:

Below is one proven method to do it. In short, you’ll want to modify your public search form template so that—just after the “Search titles only” checkbox—you add a similar checkbox for “Display results as threads.” Then (if you want the search to actually work that way) you’ll need to adjust your search‐logic in your search controller to check for the new parameter. For example:
  1. Edit the Search Form Template:
     • In your Admin CP go to Appearance → Templates and open the template that renders your main search box (typically the public:search_form template).
     • Locate the block that outputs “Search titles only.” It may look roughly like this:
Code:
<xf:if is="$canTitleLimit">
    <li>
        <xf:checkbox standalone="true">
            <xf:option name="c[title_only]" selected="{$input.c.title_only}">
                <xf:label>
                    {{ phrase('search_titles_only') }}
                    <xf:if is="$xf.options.enableTagging">
                        <span tabindex="0" role="button" data-xf-init="tooltip" data-trigger="hover focus click" title="{{ phrase('tags_will_also_be_searched')|for_attr }}">
                            <xf:fa icon="far fa-question-circle" class="u-muted u-smaller" />
                        </span>
                    </xf:if>
                </xf:label>
            </xf:option>
        </xf:checkbox>
    </li>
</xf:if>

  1. Add the “Display results as threads” Option:
     • Right below that code add a similar block. For example:
Code:
<li>
    <xf:checkbox standalone="true">
        <xf:option name="c[display_threads]" selected="{$input.c.display_threads}">
            <xf:label>{{ phrase('display_results_as_threads') }}</xf:label>
        </xf:option>
    </xf:checkbox>
</li>

If you wish to show it conditionally (for example, only if the user has permission to use that view), you can wrap it in an <xf:if> as needed.
  1. Update Your Search Logic (Optional):
     • In order for the search to actually return threads rather than individual posts when this option is checked, you’ll need to modify your search controller (typically in a file such as
      library/XF/Search/Controller/Abstract.php or in your add-on’s search handler) to check if c[display_threads] is set and then adjust the query accordingly (for example, grouping results by thread).
  2. Phrases:
     • Make sure you have a phrase defined for display_results_as_threads (you can do this via the language manager). If it doesn’t already exist, add it with the text you’d like to show.
By following these steps, your main search box will now have an extra checkbox—right under “Search titles only”—letting users choose to view their results as threads.
I don't think that's right though.

I tried inserting this into the search_form_macros template and it was rejected:
Code:
<xf:macro template="search_form_macros" name="grouped"
    arg-label="{{ phrase('display_results_as_threads') }}"
    arg-input="{$input}" />

This the "grouped" code, so maybe this is what I use for page_container:
Code:
<xf:macro name="grouped" arg-label="!" arg-input="{{ [] }}">
    <xf:checkboxrow>
        <xf:option name="grouped" value="1" selected="{$input.grouped}">{$label}</xf:option>
    </xf:checkboxrow>
</xf:macro>

I tried inserting this below the "title only" snippet:
Code:
                                <!--[XF:search_menu:above_grouped]-->
                                <div class="menu-row">
                                        <xf:macro name="grouped" arg-label="!" arg-input="{{ [] }}">
                                        <xf:checkboxrow>
                                            <xf:option name="grouped" value="1" selected="{$input.grouped}">{$label}</xf:option>
                                        </xf:checkboxrow>
                                        </xf:macro>
                                </div>
But that didn't do anything.

I tried to copy the "title only" code, which means I'd only be using the "option name" line:
Code:
                                <!--[XF:search_menu:above_grouped]-->
                                <div class="menu-row">
                                    <xf:checkbox standalone="true">
                                        <xf:option name="grouped" value="1" selected="{$input.grouped}">
                                            <xf:label>
                                                {{ phrase('display_results_as_threads') }}
                                            </xf:label>
                                        </xf:option>
                                    </xf:checkbox>
                                </div>
That added the option to the correct location but checking the box does nothing so I think I do need the "macro name" line.

This triggered an Line 367: Tag checkbox contains an unexpected child element error:
Code:
                                <!--[XF:search_menu:above_grouped]-->
                                <div class="menu-row">
                                    <xf:checkbox standalone="true">
                                        <xf:macro name="grouped" arg-label="!" arg-input="{{ [] }}">
                                        <xf:option name="grouped" value="1" selected="{$input.grouped}">
                                            <xf:label>
                                                {{ phrase('display_results_as_threads') }}
                                            </xf:label>
                                        </xf:option>
                                        </xf:macro>
                                    </xf:checkbox>
                                </div>

And this caused the option to disappear completely:
Code:
                                <!--[XF:search_menu:above_grouped]-->
                                <div class="menu-row">
                                    <xf:macro name="grouped" arg-label="!" arg-input="{{ [] }}">
                                    <xf:checkbox standalone="true">
                                        <xf:option name="grouped" value="1" selected="{$input.grouped}">
                                            <xf:label>
                                                {{ phrase('display_results_as_threads') }}
                                            </xf:label>
                                        </xf:option>
                                    </xf:checkbox>
                                    </xf:macro>
                                </div>

It doesn't show up with this either:
Code:
                                <!--[XF:search_menu:above_grouped]-->
                                <div class="menu-row">
                                        <xf:macro name="grouped" arg-label="!" arg-input="{{ [] }}">
                                        <xf:checkboxrow>
                                        <xf:option name="grouped" value="1" selected="{$input.grouped}">
                                            <xf:label>
                                                {{ phrase('display_results_as_threads') }}
                                            </xf:label>
                                        </xf:option>
                                        </xf:checkboxrow>
                                        </xf:macro>
                                </div>

I fed chatgpt the contents of "page container", "form macros", and "search form post" and it's telling me to add php code to the XF search handler, which doesn't seem correct.
Code:
if (!empty($criteria['grouped'])) {
    $query->groupBy('thread_id');
}

I eventually got chatgpt to give me this code:
Code:
                                <!--[XF:search_menu:above_grouped]-->
                                <div class="menu-row">
                                    <xf:checkbox standalone="true">
                                        <xf:option name="c[grouped]" value="1" selected="{$input.c.grouped}">
                                            <xf:label>{{ phrase('display_results_as_threads') }}</xf:label>
                                        </xf:option>
                                    </xf:checkbox>
                                </div>
And that adds ?q=ozempic&c[grouped]=1&o=date to the search URL after the search, but it doesn't group the results. A properly grouped result puts ?q=ozempic&t=post&o=date&g=1 in the URL.

I fed that info to GPT and it gave me more things to try, which didn't work, and GPT finally stopped generating responses.

After comparing the contents of search_form_macros and page_container I was sure this would work, but it doesn't:
Code:
                                <!--[XF:search_menu:above_grouped]-->
                                <div class="menu-row">
                                    <xf:checkbox standalone="true">
                                        <xf:option name="grouped">
                                            <xf:label>
                                                {{ phrase('display_results_as_threads') }}
                                            </xf:label>
                                        </xf:option>
                                    </xf:checkbox>
                                </div>
 
Last edited:
DeepSeek doesn't take .zip files, and couldn't figure it out either, but their reasoning stays visible and is interesting:
Okay, the user followed the steps but isn't seeing the "g=1" in the URL and the search results aren't grouped. Let's figure out why.

First, the user added the checkbox in the page_container template using the name "grouped". But in the working example from the advanced search, the URL uses "g=1". Wait, in the search_form_post template, the macro for "grouped" uses the name "grouped", but in the URL example provided, it's "g=1". That seems inconsistent. Maybe there's a parameter mapping somewhere.

Looking at the search_form_macros.txt, the "grouped" macro uses name="grouped". But in the URL example, the parameter is "g". So maybe the form expects "g" as the parameter name, but the macro uses "grouped". That's a mismatch. The form submission might be sending "grouped=1" instead of "g=1". But the working example uses "g=1". So the parameter name should be "g" in the form.

Wait, in the user's previous attempt, they tried using name="g" but it didn't work. Let's check the search_form_post.txt. There, the grouped macro is called, which uses name="grouped". But in the advanced search form, when the checkbox is checked, it's "g=1". That suggests that the actual parameter expected by the search controller is "g", but the macro uses "grouped". How does that work?

Looking at the search_form_macros macro for "grouped", the option name is "grouped". However, in the URL, the parameter is "g=1". That implies that somewhere, the parameter "grouped" is being renamed to "g" when the form is submitted. Wait, maybe the form uses "grouped" but the controller reads it as "g"? That doesn't make sense. Alternatively, maybe the search controller is expecting the parameter "grouped" but the URL shortens it to "g" via routing or some other mechanism.

Alternatively, perhaps the search form uses "grouped" as the parameter name, and the URL displays it as "g" due to route parameters. For example, in some frameworks, form parameters can be mapped to shorter query parameters. But in XenForo, the search form's action is to "search/search", and the parameters are passed directly. So if the form has an input named "grouped", the URL should show "grouped=1", not "g=1". But in the user's example, the working URL has "g=1", which suggests that the correct parameter name is "g".

Therefore, the mistake is in the name of the checkbox. The user's code uses name="grouped", but it should be name="g" to match the parameter in the URL. That's why adding the checkbox with name="g" would make the URL show "g=1".

But wait, in the search_form_post.txt, the macro uses name="grouped", but when the form is submitted, it becomes "g=1". How is that possible? Let me check the search_form_post.txt again. The macro is called with:

Code:
<xf:macro template="search_form_macros" name="grouped" arg-label="{{ phrase('display_results_as_threads') }}" arg-input="{$input}" />


And the macro in search_form_macros.txt is:


<xf:macro name="grouped" arg-label="!" arg-input="{{ [] }}">


<xf:checkboxrow>


<xf:option name="grouped" value="1" selected="{$input.grouped}">{$label}</xf:option>


</xf:checkboxrow>


</xf:macro>

So the input name is "grouped", but in the URL it's "g=1". That means there's a discrepancy. How does the "grouped" parameter become "g" in the URL? Maybe the search controller is reading the "grouped" parameter and then using it to set the "g" parameter in the URL. Alternatively, maybe the route for the search is configured to accept "g" as a shorthand for "grouped".

But in reality, when you submit a form with a parameter named "grouped", the URL would have "grouped=1", not "g=1". The user's example shows that the advanced search form uses "g=1", which suggests that the correct parameter name is "g". Therefore, the macro in search_form_macros.txt must be using "name=g" instead of "name=grouped". But in the provided code, the macro uses "name=grouped".

Wait, looking back, the user mentioned that in the advanced search form, the URL shows "g=1", but in the search_form_post.txt, the macro uses "grouped". This inconsistency suggests that the actual parameter name is "g", and the macro in the advanced search form must be using "name=g" to get that result. Therefore, the user's mistake is using "name=grouped" instead of "name=g".

So, to fix the issue, the checkbox in the main search menu should have the name "g" instead of "grouped". That way, when the form is submitted, it will include "g=1" in the URL, and the search controller will recognize it, leading to grouped results.

Additionally, the user tried using "name=g" but it didn't work. Why? Maybe because the search controller's action expects the parameter to be passed in a certain way. Let me check the XenForo search controller code (hypothetically, since I can't access it). Typically, in XenForo, the search forms use the parameter names directly. So if the advanced search uses "g=1" and it's generated by a macro with "name=g", then the main search form should also use "name=g".

Therefore, the correct code for the checkbox in the page_container template should be:

<xf:option name="g" value="1" selected="{$input.g}">

This would make the form submit "g=1" when checked, matching the advanced search's parameter. The user's previous attempts used "name=g", but maybe they didn't include the "selected" attribute correctly, or there's another issue.

Another possibility is that the main search form's action doesn't process the "g" parameter. The main search menu is part of the quick search form, which might not include all the parameters that the advanced search form does. The advanced search form might be using a different controller action that processes the "g" parameter, while the quick search form does not.

To confirm, the main search form in page_container.txt is part of the quick search dropdown. Its form action is:

<form action="{{ link('search/search') }}" method="post" class="menu-content" data-xf-init="quick-search">

The advanced search form in search_form_post.txt is part of the advanced search page, which might submit to the same action but includes additional parameters. However, the quick search form might not be set up to handle the "g" parameter unless explicitly added.

Therefore, even if the "g" parameter is included in the form, the quick search might not process it unless the controller action is modified or the parameter is whitelisted. However, since the advanced search works, it's likely that the parameter is recognized by the search controller, but the quick search form needs to include it.

In that case, adding the "g" parameter to the quick search form should work. So the user needs to adjust the checkbox to use "name=g" instead of "grouped".

So the correct code in page_container.txt should be:

Code:
<!--[XF:search_menu:above_grouped]-->


<div class="menu-row">


<xf:checkbox standalone="true">


<xf:option name="g" value="1" selected="{$input.g}">


<xf:label>


{{ phrase('display_results_as_threads') }}


</xf:label>


</xf:option>


</xf:checkbox>


</div>

This way, when the checkbox is checked, the form will submit "g=1", which should trigger the grouped results. The user's previous attempts might have used "name=g" but without the "selected" attribute correctly referencing the input data. Using "selected="{$input.g}"" ensures that the checkbox state is preserved correctly.

Additionally, the user should verify that the search controller's action for the quick search (handled by the \XF\Pub\Controller\Search::actionSearch() method) accepts and processes the "g" parameter. If the parameter is not being processed, even if it's in the URL, the results won't group. However, since the advanced search works with "g=1", it's likely that the parameter is recognized in both contexts.

Another thing to check is that the template modification is placed correctly in the form. If the checkbox is added outside the form or in a different part of the template, it might not be included in the submission. The user should ensure that the new checkbox is within the <form> tags of the quick search menu.

In summary, the key issues are:

1. The checkbox should use name="g" instead of "grouped".

2. The selected attribute should reference $input.g.

3. The checkbox must be within the search form's <form> element.

I needed that public reasoning to see the (handled by the \XF\Pub\Controller\Search::actionSearch() method) reference, because they didn't include it in their answer (which was wrong).

I then fed it the contents of that file and it changed the output to this, which still doesn't work:
Code:
<!--[XF:search_menu:above_grouped]-->
<div class="menu-row">
    <xf:checkbox standalone="true">
        <xf:option name="grouped" value="1" selected="{$input.grouped}">
            <xf:label>
                {{ phrase('display_results_as_threads') }}
            </xf:label>
        </xf:option>
    </xf:checkbox>
</div>
This was the reasoning:
Okay, let's try to figure out why the "Display results as threads" option isn't working even after adding the checkbox to the template. The user mentioned that adding the checkbox doesn't include "g=1" in the URL and the results aren't grouped.

First, I'll check the code they added. They used <xf:option name="g" value="1" selected="{$input.g}"> in the page_container template. The parameter name is "g", which aligns with the URL they observed in the advanced search. But when they submit the form, "g=1" isn't appearing.

Looking at the Search.php controller, specifically the actionSearch() method, there's a filter for 'grouped' as a boolean. The code processes $input = $this->filter($filters); where $filters includes 'grouped' => 'bool'. However, in the template, the checkbox is named "g", not "grouped". This mismatch means the controller isn't receiving the "g" parameter.

Wait, but in the convertShortSearchInputNames() method, there's a line that maps 'g' from the request to 'grouped' in the input array. So, the 'g' parameter from the URL is converted to 'grouped' in the search data. That means the controller expects the checkbox to be named "g", which is what the user has. But why isn't it being captured?

Looking at the prepareSearchQuery() method, it filters 'grouped' as a boolean from the input data. But the form submission might not be including the "g" parameter because the checkbox is named "g", and the form is using POST. Let me check how the parameters are being passed.

In the convertShortSearchInputNames() method, the code converts the query parameters (GET) into the input array. But when the form is submitted via POST, the parameters are in the POST data. The actionSearch() method uses $this->filter($filters) which includes 'grouped' as a boolean. However, the form's checkbox is named "g", so the POST data would have "g=1", but the controller is looking for "grouped" in the POST data.

Ah, there's a discrepancy here. The convertShortSearchInputNames() method is used to convert short parameter names (like 'g' from the URL) into the full names (like 'grouped') for internal processing. However, when submitting the form via POST, the controller's actionSearch() method directly filters the 'grouped' parameter, expecting it to come from the form as "grouped", not "g".

Therefore, the checkbox in the form should be named "grouped" instead of "g" to match the controller's expected parameter name. The URL uses "g=1" because of the convertShortSearchInputNames() method, which maps "g" to "grouped". But in a POST request, the form field should be named "grouped" to be correctly filtered by the controller.

So, changing the checkbox's name from "g" to "grouped" in the template should make the controller recognize the parameter. The convertShortSearchInputNames() handles the URL parameter "g" for GET requests, but POST requests need the field named "grouped" to be processed correctly.

I then gave it the contents of SearchForum.php, and it reasoned this:
Okay, the user is trying to add a "Display results as threads" checkbox to the main search box in XenForo 2.2. They want this option to appear below "Search titles only" and have it function correctly by adding "g=1" to the URL and grouping the search results. They've tried modifying the page_container template but it's not working.



First, I need to understand where the problem lies. The user added the checkbox using the name "grouped" as per the controller's expectations, but it's still not working. Let me check the Search.php controller they provided. In the actionSearch method, there's a filter for 'grouped' as a boolean. That means the form should submit a parameter named "grouped". The convertShortSearchInputNames method maps 'g' from the URL to 'grouped' internally. So, when submitting via POST, the form should use "grouped", but in the URL, it's "g=1".



Wait, the user tried using both "g" and "grouped" in the checkbox name. When they used "grouped", the URL didn't show "g=1". Maybe the issue is that after the search is processed, the redirect uses the parameters from the search query, which might not include "g" unless it's mapped correctly. Let me check the runSearch method. It constructs the redirect using the search entity, which stores 'search_grouping'. The convertSearchToQueryInput method maps 'search_grouping' to 'grouped' in the input array, which then gets converted to 'g' in the URL via convertShortSearchInputNames. So, if the search is saved with grouped=1, the redirect should include g=1.



But why isn't the checkbox state being saved? Let me look at the prepareSearchQuery method. It takes the input data, including 'grouped', and passes it to the query. The query's withGroupedResults is called if 'grouped' is true. Then, when the search is saved, 'search_grouping' is set based on 'grouped'. So, the redirect should have 'g=1' because convertShortSearchInputNames translates 'grouped' to 'g'.



Wait, but the user said that adding the checkbox with name="grouped" didn't add "g=1" to the URL. Maybe the issue is that the template modification isn't within the correct form. The search form in page_container might not be submitting the parameter correctly. Let me check the form in page_container.txt. The form action is link('search/search'), method post, and data-xf-init="quick-search". The checkboxes need to be inside this form.



The user added the new checkbox in the correct place, but maybe there's a JavaScript issue with the quick-search form. The quick-search might not be including all form elements when submitting. Alternatively, maybe the 'grouped' parameter isn't being included in the form data sent via AJAX. Let me check the quick-search JavaScript. If it's using data from the form, it should include all input elements. But if the grouped checkbox is added dynamically or not part of the form's serialization, it might be missed.



Another possibility is that the template modification is placed outside the form or in a location where it's not rendered. The user should ensure that the new checkbox is within the <form> tags of the quick-search dropdown. Let me verify the structure in page_container.txt. The form is inside the menu-content div. The user placed the new checkbox after the title-only section, which is within the form. So placement should be correct.



Wait, maybe the 'grouped' parameter is not being passed to the search query. Let me check the prepareSearchQuery method again. It checks $input['grouped'] and calls $query->withGroupedResults() if true. So if the checkbox is checked, 'grouped' should be 1 in the POST data, leading to withGroupedResults being called. Then, when the search is saved, 'search_grouping' is set, and the redirect URL should have 'g=1'.



But the user isn't seeing 'g=1' in the URL. That suggests that either the 'grouped' parameter isn't being submitted, or the search isn't being saved correctly. Let me check the form submission. Using browser dev tools, the user can inspect the network request when submitting the search. They should check if 'grouped=1' is present in the POST data. If it is, then the issue is in how the redirect URL is constructed. If not, the checkbox isn't part of the form.



Another angle: Maybe the 'grouped' option is only applicable to certain search types. For example, in the advanced search under "Search threads", it works because the search type is 'post'. The main search might default to a different type where grouping isn't applied. Let me check the search_type parameter. The main search form might not specify 't=post', so the grouped parameter is ignored unless the search type is appropriate.



In the Search.php controller, when preparing the query, if the search_type is not set to 'post', maybe grouping isn't applied. Let me check the prepareSearchQuery method. If the search_type is not set, the query might not apply grouping. The user needs to ensure that the search type is compatible with grouping. For example, when searching threads (t=post), grouping is valid, but for other content types, it might not be.



So, to fix this, the main search form's quick-search should include the 't=post' parameter to ensure the search is for threads, allowing the grouped parameter to take effect. The user can add a hidden input field to set the search type to 'post' when the grouped checkbox is present.



Alternatively, in the page_container template, modify the form to include <input type="hidden" name="t" value="post" /> when the grouped checkbox is added. This would force the search type to 'post', making the grouped parameter valid.



Testing this would involve adding the hidden input and checking if the 'g=1' appears in the URL and results are grouped. If that works, then the issue was the missing search type parameter. The user can then adjust the template modification to include both the checkbox and the hidden input.

And came up with this, which still didn't work:
Code:
                                <!--[XF:search_menu:above_grouped]-->
                                <div class="menu-row">
                                    <xf:checkbox standalone="true">
                                        <xf:option name="grouped" value="1" selected="{$input.grouped}">
                                            <xf:label>
                                                {{ phrase('display_results_as_threads') }}
                                            </xf:label>
                                        </xf:option>
                                    </xf:checkbox>
                                </div>
                                <input type="hidden" name="t" value="post" /> <!-- Force search type to "threads" -->

Maybe if it could analyze the full .zip XF source code it would do better than GPT.
 
Back
Top Bottom