Lack of interest Add vertical display macro to admin helper_criteria

This suggestion has been closed automatically because it did not receive enough votes over an extended period of time. If you wish to see this, please search for an open suggestion and, if you don't find any, post a new one.

Snog

Well-known member
I don't know how many people will recall a discussion about this while XF 2.0 was in developer preview, but this goes way back to then.

I (and a couple of other developers) use a rather down and dirty preg_replace that looks for the page panes in helper_criteria to change the display so it can be viewed vertically in a tab in our own add-ons. That preg_replace needs to be fairly specific as to what is replaced. This has worked without a problem since the developer preview. However, other developers are starting to add panes to the helper_criteria template and that is breaking the preg_replace because what is added is completely unknown, arbitrary and can't be accounted for.

With that in mind, I suggest adding a user_page macro to helper_criteria which would allow displaying the user criteria vertically on any page a developer might want to put it on. And then it's up to each add-on developer to add their code to it if they want what they add to appear there.

This has been tested and does work:
Code:
<xf:macro name="user_page" arg-container="" arg-active="" arg-criteria="!" arg-data="!">

    <xf:set var="$app" value="{$xf.app}" />
    <xf:set var="$visitor" value="{$xf.visitor}" />
    <xf:set var="$em" value="{$app.em}" />

    <xf:set var="$page">
        <!--[XF:user:top]-->

        <xf:checkboxrow label="{{ phrase('privileges_and_status') }}">
            <!--[XF:user:status_top]-->

            <xf:option name="user_criteria[is_guest][rule]" value="is_guest" selected="{$criteria.is_guest}"
                label="{{ phrase('user_is_guest') }}" />
            <xf:option name="user_criteria[is_logged_in][rule]" value="is_logged_in" selected="{$criteria.is_logged_in}"
                label="{{ phrase('user_is_logged_in') }}" />
            <xf:option name="user_criteria[is_moderator][rule]" value="is_moderator" selected="{$criteria.is_moderator}"
                label="{{ phrase('user_is_moderator') }}" />
            <xf:option name="user_criteria[is_admin][rule]" value="is_admin" selected="{$criteria.is_admin}"
                label="{{ phrase('user_is_administrator') }}" />
            <xf:option name="user_criteria[is_banned][rule]" value="is_banned" selected="{$criteria.is_banned}"
                label="{{ phrase('user_is_banned') }}"/>
            <xf:option name="user_criteria[birthday][rule]" value="birthday" selected="{$criteria.birthday}"
                label="{{ phrase('user_birthday_is_today') }}"/>
            <xf:option name="user_criteria[user_state][rule]" value="user_state" selected="{$criteria.user_state}"
                label="{{ phrase('user_state_is:') }}">

                <xf:dependent>
                    <xf:select name="user_criteria[user_state][data][state]" value="{$criteria.user_state.state}">
                        <xf:option value="valid">{{ phrase('valid') }}</xf:option>
                        <xf:option value="email_confirm">{{ phrase('awaiting_email_confirmation') }}</xf:option>
                        <xf:option value="email_confirm_edit">{{ phrase('awaiting_email_confirmation_from_edit') }}</xf:option>
                        <xf:option value="email_bounce">{{ phrase('email_invalid_bounced') }}</xf:option>
                        <xf:option value="moderated">{{ phrase('awaiting_approval') }}</xf:option>
                        <xf:option value="rejected">{{ phrase('rejected') }}</xf:option>
                        <xf:option value="disabled">{{ phrase('disabled') }}</xf:option>
                    </xf:select>
                </xf:dependent>

            </xf:option>

            <!--[XF:user:status_bottom]-->
        </xf:checkboxrow>

        <hr class="formRowSep" />

        <!--[XF:user:after_status]-->

        <xf:checkboxrow label="{{ phrase('connected_accounts') }}">
            <xf:option name="user_criteria[connected_accounts][rule]" value="connected_accounts" selected="{$criteria.connected_accounts}"
                label="{{ phrase('user_is_associated_with_any_of_selected_connected_account_providers:') }}">

                <xf:select name="user_criteria[connected_accounts][data][provider_ids]" size="4" multiple="true" value="{$criteria.connected_accounts.provider_ids}">
                    <xf:options source="$data.connectedAccProviders" />
                </xf:select>

            </xf:option>
        </xf:checkboxrow>

        <hr class="formRowSep" />

        <!--[XF:user:after_connected]-->

        <xf:checkboxrow label="{{ phrase('user_groups') }}">
            <xf:option name="user_criteria[user_groups][rule]" value="user_groups" selected="{$criteria.user_groups}"
                label="{{ phrase('user_is_member_of_any_of_selected_user_groups:') }}">

                <xf:select name="user_criteria[user_groups][data][user_group_ids]" size="4" multiple="true" value="{$criteria.user_groups.user_group_ids}">
                    <xf:foreach loop="$data.userGroups" key="$userGroupId" value="$userGroupTitle">
                        <xf:option value="{$userGroupId}">{$userGroupTitle}</xf:option>
                    </xf:foreach>
                </xf:select>

            </xf:option>

            <xf:option name="user_criteria[not_user_groups][rule]" value="not_user_groups" selected="{$criteria.not_user_groups}"
                label="{{ phrase('user_is_not_member_of_any_of_selected_user_groups:') }}">

                <xf:select name="user_criteria[not_user_groups][data][user_group_ids]" size="4" multiple="true" value="{$criteria.not_user_groups.user_group_ids}">
                    <xf:foreach loop="$data.userGroups" key="$userGroupId" value="$userGroupTitle">
                        <xf:option value="{$userGroupId}">{$userGroupTitle}</xf:option>
                    </xf:foreach>
                </xf:select>

            </xf:option>
        </xf:checkboxrow>

        <hr class="formRowSep" />

        <!--[XF:user:after_groups]-->

        <xf:checkboxrow label="{{ phrase('content_and_achievements') }}">
            <!--[XF:user:content_top]-->

            <xf:option name="user_criteria[messages_posted][rule]" value="messages_posted" selected="{$criteria.messages_posted}"
                label="{{ phrase('user_has_posted_at_least_x_messages:') }}">
                <xf:numberbox name="user_criteria[messages_posted][data][messages]" value="{$criteria.messages_posted.messages}"
                    size="5" min="0" step="1" />
            </xf:option>

            <xf:option name="user_criteria[messages_maximum][rule]" value="messages_maximum" selected="{$criteria.messages_maximum}"
                label="{{ phrase('user_has_posted_no_more_than_x_messages:') }}">
                <xf:numberbox name="user_criteria[messages_maximum][data][messages]" value="{$criteria.messages_maximum.messages}"
                    size="5" min="0" step="1" />
            </xf:option>

            <!--[XF:user:content_after_messages]-->

            <xf:option name="user_criteria[reaction_score][rule]" value="reaction_score" selected="{$criteria.reaction_score}"
                label="{{ phrase('user_has_received_a_reaction_sore_of_at_least_x:') }}">
                <xf:numberbox name="user_criteria[reaction_score][data][reactions]" value="{$criteria.reaction_score.reactions}"
                    size="5" min="0" step="1" />
            </xf:option>

            <xf:option name="user_criteria[reaction_ratio][rule]" value="reaction_ratio" selected="{$criteria.reaction_ratio}"
                label="{{ phrase('user_reaction_message_ratio_is_at_least:') }}">
                <xf:numberbox name="user_criteria[reaction_ratio][data][ratio]" value="{$criteria.reaction_ratio.ratio}"
                    size="5" min="0" step="0.25" />
                <xf:afterhint>{{ phrase('reaction_message_ratio_explanation') }}</xf:afterhint>
            </xf:option>

            <!--[XF:user:content_after_reactions]-->

            <xf:option name="user_criteria[trophy_points][rule]" value="trophy_points" selected="{$criteria.trophy_points}"
                label="{{ phrase('user_has_at_least_x_trophy_points:') }}">
                <xf:numberbox name="user_criteria[trophy_points][data][points]" value="{$criteria.trophy_points.points}"
                    size="5" min="0" step="1" />
            </xf:option>

            <!--[XF:user:content_after_trophies]-->

            <xf:option name="user_criteria[registered_days][rule]" value="registered_days" selected="{$criteria.registered_days}"
                label="{{ phrase('user_has_been_registered_for_at_least_x_days:') }}">
                <xf:numberbox name="user_criteria[registered_days][data][days]" value="{$criteria.registered_days.days}"
                    size="5" min="0" step="1" />
            </xf:option>

            <xf:option name="user_criteria[inactive_days][rule]" value="inactive_days" selected="{$criteria.inactive_days}"
                label="{{ phrase('user_has_not_visited_for_at_least_x_days:') }}">
                <xf:numberbox name="user_criteria[inactive_days][data][days]" value="{$criteria.inactive_days.days}"
                    size="5" min="0" step="1" />
            </xf:option>

            <!--[XF:user:content_bottom]-->
        </xf:checkboxrow>

        <hr class="formRowSep" />

        <!--[XF:user:after_content]-->

        <xf:checkboxrow label="{{ phrase('user_profile_and_options') }}">
            <!--[XF:user:profile_top]-->

            <xf:option name="user_criteria[language][rule]" value="language" selected="{$criteria.language}"
                label="{{ phrase('user_is_browsing_with_following_language:') }}">

                <xf:select name="user_criteria[language][data][language_id]" value="{$criteria.language.language_id}">
                    <xf:foreach loop="$data.languageTree.getFlattened(0)" value="$treeEntry">
                        <xf:option value="{$treeEntry.record.language_id}">{{ repeat('--', $treeEntry.depth) }} {$treeEntry.record.title}</xf:option>
                    </xf:foreach>
                </xf:select>

            </xf:option>

            <xf:optgroup label="{{ phrase('avatar:') }}">
                <xf:option name="user_criteria[has_avatar][rule]" value="has_avatar" selected="{$criteria.has_avatar}"
                    label="{{ phrase('user_has_avatar') }}" />

                <xf:option name="user_criteria[no_avatar][rule]" value="no_avatar" selected="{$criteria.no_avatar}"
                    label="{{ phrase('user_has_no_avatar') }}" />
            </xf:optgroup>

            <xf:optgroup label="{{ phrase('high_resolution_avatar:') }}">
                <xf:option name="user_criteria[has_highdpi_avatar][rule]" value="has_highdpi_avatar" selected="{$criteria.has_highdpi_avatar}"
                    label="{{ phrase('user_has_highdpi_avatar') }}" />

                <xf:option name="user_criteria[no_highdpi_avatar][rule]" value="no_highdpi_avatar" selected="{$criteria.no_highdpi_avatar}"
                    label="{{ phrase('user_has_no_highdpi_avatar') }}" />
            </xf:optgroup>

            <xf:optgroup label="{{ phrase('two_step_verification:') }}">
                <xf:option name="user_criteria[with_tfa][rule]" value="with_tfa" selected="{$criteria.with_tfa}"
                    label="{{ phrase('user_has_enabled_two_step_verification') }}" />

                <xf:option name="user_criteria[without_tfa][rule]" value="without_tfa" selected="{$criteria.without_tfa}"
                    label="{{ phrase('user_has_not_enabled_two_step_verification') }}"/>
            </xf:optgroup>

            <!--[XF:user:profile_bottom]-->
        </xf:checkboxrow>

        <hr class="formRowSep" />

        <!--[XF:user:after_profile]-->

        <xf:checkboxrow label="{{ phrase('specific_users') }}">
            <!--[XF:user:specific_top]-->

            <xf:option name="user_criteria[username][rule]" value="username" selected="{$criteria.username}"
                label="{{ phrase('username_is:') }}">
                <xf:textbox name="user_criteria[username][data][names]" value="{$criteria.username.names}" ac="true" />
                <xf:afterhint>{{ phrase('username_criteria_explain') }}</xf:afterhint>

            </xf:option>

            <xf:option name="user_criteria[username_search][rule]" value="username_search" selected="{$criteria.username_search}"
                label="{{ phrase('username_contains:') }}">
                <xf:textbox name="user_criteria[username_search][data][needles]" value="{$criteria.username_search.needles}" />
                <xf:afterhint>{{ phrase('username_search_criteria_explain') }}</xf:afterhint>
            </xf:option>

            <xf:option name="user_criteria[email_search][rule]" value="email_search" selected="{$criteria.email_search}"
                label="{{ phrase('email_address_contains:') }}">
                <xf:textbox name="user_criteria[email_search][data][needles]" value="{$criteria.email_search.needles}" />
                <xf:afterhint>{{ phrase('email_search_criteria_explain') }}</xf:afterhint>
            </xf:option>

            <!--[XF:user:specific_bottom]-->
        </xf:checkboxrow>

        <!--[XF:user:bottom]-->

        <h3 class="block-formSectionHeader">
            <span class="collapseTrigger collapseTrigger--block" data-xf-click="toggle" data-target="< :up:next">
                <span class="block-formSectionHeader-aligner"><b>{{ phrase('custom_userfield_criteria')}}</b></span>
            </span>
        </h3>

        <div class="block-body block-body--collapsible">
            <xf:if contentcheck="true">
                <xf:contentcheck>
                    <xf:foreach loop="$xf.app.em.getRepository('XF:UserField').getDisplayGroups()" key="$fieldGroup" value="$groupPhrase">

                        <xf:set var="$customFields" value="{$app.getCustomFields('users', $fieldGroup)}" />
                        <xf:if contentcheck="true">
                            <h2 class="block-formSectionHeader"><span class="block-formSectionHeader-aligner">{$groupPhrase}</span></h2>
                            <xf:contentcheck>
                                <xf:foreach loop="$customFields" key="$fieldId" value="$fieldDefinition">
                                    <xf:set var="$fieldName" value="user_field_{$fieldId}" />
                                    <xf:set var="$choices" value="{$fieldDefinition.field_choices}" />
                                    <xf:checkboxrow label="{$fieldDefinition.title}">
                                        <xf:option name="user_criteria[{$fieldName}][rule]" value="{$fieldName}" selected="{$criteria.{$fieldName}}"
                                            label="{{ $choices ? phrase('criteria_userfield_choice_among') : phrase('criteria_userfield_contains_text:') }}">
                                            <xf:dependent>
                                                <xf:if is="!{$choices}">
                                                    <xf:textbox name="user_criteria[{$fieldName}][data][text]" value="{$criteria.{$fieldName}.text}" />
                                                <xf:elseif is="{{ count($choices) }} > 6" />
                                                    <xf:select name="user_criteria[{$fieldName}][data][choices]" value="{$criteria.{$fieldName}.choices}" multiple="multiple" size="5">
                                                        <xf:options source="{$choices}" />
                                                    </xf:select>
                                                <xf:else />
                                                    <xf:checkbox name="user_criteria[{$fieldName}][data][choices]" value="{$criteria.{$fieldName}.choices}" listclass="listColumns">
                                                        <xf:options source="{$choices}" />
                                                    </xf:checkbox>
                                                </xf:if>
                                            </xf:dependent>
                                        </xf:option>
                                    </xf:checkboxrow>
                                </xf:foreach>
                            </xf:contentcheck>
                        </xf:if>

                    </xf:foreach>
                </xf:contentcheck>
            <xf:else />
                {{ phrase('no_custom_fields_have_been_created_yet') }}
            </xf:if>
        </div>
    </xf:set>

    {$page|raw}
</xf:macro>

It could be called either in a template or in PHP like this:
Code:
protected function buildPermissionDisplay($entity)
{
    // BUILD USER CRITERIA HELPER FOR A SINGLE PAGE (NO PANES/TABS)
    $userCriteria = $this->app->criteria('XF:User', $entity->user_criteria);
    $xfTemplater = $this->app->templater();
    $xfTemplater->addDefaultParam('xf', $this->app->getGlobalTemplateData());

    $userCriteriaRendered = $xfTemplater->renderMacro(
        'admin:helper_criteria',
        'user_page', [
            'criteria' => $userCriteria->getCriteriaForTemplate(),
            'data' => $userCriteria->getExtraTemplateData()
        ]
    );

    return $userCriteriaRendered;
}

It results in this when the results are wrapped properly:
merged.webp

Of course developers would need to wrap the user_page macro results in the appropriate HTML for their use case.
 
Last edited:
Upvote 6
This suggestion has been closed. Votes are no longer accepted.
Just trying to make my case here. ;)

If people continue to add tabs to the user criteria, and an add-on already has quite a few tabs, it becomes almost certain that users will miss something in the critical tabs for the add-on. For example, this is my forms add-on tabs without a vertical display of the criteria...

formtabs.webp

This is the same add-on with a vertical criteria display and a single tab for the User Criteria...

verttab.webp

As you can see, if other add-ons continue to add panes to the user criteria that would push critical portions of the tabs out of site no matter where the panes are located.
 
Top Bottom