Plugin Directory

Changeset 3403091


Ignore:
Timestamp:
11/26/2025 07:34:21 AM (3 months ago)
Author:
eitanatbrightleaf
Message:

Update to version 1.0.1 from GitHub

Location:
gravityops-search
Files:
6 edited
1 copied

Legend:

Unmodified
Added
Removed
  • gravityops-search/tags/1.0.1/gravityops-search.php

    r3363746 r3403091  
    33 * Plugin Name: GravityOps Search
    44 * Description: A shortcode to search and display Gravity Forms entries based on specified criteria and attributes.
    5  * Version: 1.0.0
     5 * Version: 1.0.1
    66 * Author: BrightLeaf Digital
    7  * Author URI: https://digital.brightleaf.info/
     7 * Author URI: https://brightleafdigital.io/
     8 * Plugin URI: https://brightleafdigital.io/gravityops-search/
    89 * License: GPL-2.0+
    910 */
  • gravityops-search/tags/1.0.1/includes/class-gravityops-search.php

    r3363746 r3403091  
    2525     * @var string Version of the plugin.
    2626     */
    27     protected $_version = '1.0.0';
     27    protected $_version = '1.0.1';
    2828
    2929    /**
     
    3232     * @var string Plugin slug.
    3333     */
    34     protected $_slug = 'gravops_search';
     34    protected $_slug = 'gravityops_search';
    3535
    3636    /**
     
    5353     * @var string Short version of the plugin title to be used in menus.
    5454     */
    55     protected $_short_title = 'GravOps Search';
     55    protected $_short_title = 'GravityOps Search';
    5656    // phpcs:enable PSR2.Classes.PropertyDeclaration.Underscore
    5757
     
    7777    }
    7878
    79     /**
     79    /**
     80     * Initializes the admin functionalities for the application.
     81     *
     82     * Sets up the necessary hooks and actions to configure the admin area, including adding the top-level menu.
     83     *
     84     * @return void This method does not return any value.
     85     */
     86    public function init_admin() {
     87        parent::init_admin();
     88        add_action( 'admin_menu', [ $this, 'add_top_level_menu' ] );
     89    }
     90
     91    /**
     92     * Add a top-level menu in the WordPress admin.
     93     *
     94     * @return void
     95     */
     96    public function add_top_level_menu() {
     97        global $menu;
     98
     99        $has_full_access = current_user_can( 'gform_full_access' );
     100        $min_cap         = GFCommon::current_user_can_which( $this->_capabilities_app_menu );
     101        if ( empty( $min_cap ) ) {
     102            $min_cap = 'gform_full_access';
     103        }
     104
     105        // if another plugin in our suit is already installed and created the submenu we don't have to.
     106        if ( in_array( 'gravity_ops', array_column( $menu, 2 ), true ) ) {
     107            add_submenu_page(
     108                'gravity_ops',
     109                $this->_short_title,
     110                $this->_short_title,
     111                $has_full_access ? 'gform_full_access' : $min_cap,
     112                $this->_slug,
     113                [ $this, 'create_sub_menu' ]
     114            );
     115
     116            return;
     117        }
     118
     119        $number        = 10;
     120        $menu_position = '16.' . $number;
     121        while ( isset( $menu[ $menu_position ] ) ) {
     122            $number       += 10;
     123            $menu_position = '16.' . $number;
     124        }
     125
     126        $this->app_hook_suffix = add_menu_page(
     127            'GravityOps',
     128            'GravityOps',
     129            $has_full_access ? 'gform_full_access' : $min_cap,
     130            'gravity_ops',
     131            [ $this, 'create_top_level_menu' ],
     132            $this->get_app_menu_icon(),
     133            $menu_position
     134        );
     135        add_submenu_page(
     136            'gravity_ops',
     137            $this->_short_title,
     138            $this->_short_title,
     139            $has_full_access ? 'gform_full_access' : $min_cap,
     140            $this->_slug,
     141            [
     142                $this,
     143                'create_sub_menu',
     144            ]
     145        );
     146    }
     147
     148    /**
     149     * Retrieves the SVG icon for the application menu in a base64-encoded string.
     150     *
     151     * The method generates an SVG icon XML, encodes it in base64, and formats it as a data URL
     152     * suitable for use as an image source in web applications.
     153     *
     154     * @return string The base64-encoded SVG icon as a data URL.
     155     */
     156    public function get_app_menu_icon() {
     157        $svg_xml = '<?xml version="1.0" encoding="utf-8"?><svg height="24" id="Layer_1" viewBox="0 0 300 300" width="24" xmlns="http://www.w3.org/2000/svg" >
     158<defs>
     159<style>
     160      .cls-1 {
     161        fill: #fff;
     162      }
     163      .cls-4 {
     164        fill: #fff;
     165      }
     166    </style>
     167<radialGradient cx="-28.79" cy="-50.67" fx="-28.79" fy="-50.67" gradientTransform="translate(.26 .38) scale(1.05)" gradientUnits="userSpaceOnUse" id="radial-gradient" r="433.22">
     168<stop offset="0" stop-color="#402a56"/>
     169<stop offset="1" stop-color="#2f2e41"/>
     170</radialGradient>
     171</defs>
     172<g>
     173<g>
     174<path class="cls-4" d="M204.44,45.16c-7.84,2.35-15.26,5.96-22.05,10.2,0,0-.02,0-.03.01-15.43,9.64-27.63,22.58-34.25,31.59-9.53,13-27.14,30.42-43.32,13.65-2.65-2.75-4.19-6.14-4.72-9.87-1.88-13.02,8.47-30.17,26.39-38.44,33.79-15.6,95.3-12.35,77.98-7.15Z" fill="black"/>
     175<path class="cls-1" d="M214.25,50.81c-4.41,2.77-11.39,11-16.43,17.33,0,0,0,0-.01,0-1.67,2.09-3.13,3.98-4.21,5.39-11.02,14.34-31.85,47.1-37.9,60.65-8.26,18.49-36.2,49.52-61.36,35.86-.16-.08-.32-.18-.47-.27-.04-.02-.08-.05-.12-.06-25.34-14.5-19.28-50.67,2.72-74.12-8.81,13.47-6.66,25.45.75,32.32,17.55,16.25,36.77,2.62,47.34-13.87,8.15-12.72,17.71-24.76,28.14-34.82,8.38-8.08,23.51-19.35,32.73-24.2,3.09-1.64,7.15-3.25,8.83-4.2Z" fill="black"/>
     176<path class="cls-1" d="M221.42,60.81c-.66,1.3-5.48,10.14-10.42,20.46t0,.01c-3.67,7.67-7.41,16.16-9.58,23-4.32,13.6-16.91,56.93-19.49,64.57-4.83,14.29-11.87,24.53-20.51,31.19-.29.23-.58.44-.88.66-9.4,6.88-20.63,9.65-32.99,8.88-15.67-.98-27.53-10.99-31.65-27.29,2.63,5.35,7.76,9.4,16.05,10.18,17.18,1.61,29.48-5.6,37.79-13.93,2.9-2.9,5.31-5.95,7.27-8.81,7.58-11.05,20.74-47.79,28.81-63.68,15.38-30.3,27.18-36.6,35.61-45.22Z" fill="black"/>
     177<path class="cls-1" d="M223.33,174.26h0c-.01.29-.03.58-.05.87-1.12,21.48-14.24,36.62-31.35,38.34-12.52,1.25-24.18-3-31.41-12.78.29-.21.58-.43.88-.66,3.05,1.98,6.75,3.07,11.19,3.03,22.82-.2,31.59-25.49,32.65-44.19,3.54-62.38,17.03-82.68,18.03-85.08-.29,4.36-4.98,17.58-5.62,30.49-.18,3.55-.23,7-.19,10.35h0c.27,21.03,4.28,38.11,5.6,51.39.28,2.83.36,5.58.27,8.23Z" fill="black"/>
     178<path class="cls-1" d="M241.9,175.78c-7.01,2.69-13.2,2.1-18.62-.65.02-.29.03-.58.05-.86,2.51.46,5.02.16,7.53-.96,11.48-5.11,7.91-25.36,3.03-36.08-4.65-10.23-7.63-25.56-8.77-44.1,5.25,23.34,16.89,31.95,23.93,41.17,6.73,8.81,16.03,32.6-7.15,41.48Z" fill="black"/>
     179</g>
     180</g>
     181</svg>';
     182        return sprintf( 'data:image/svg+xml;base64,%s', base64_encode( $svg_xml ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
     183    }
     184
     185    /**
     186     * Outputs the HTML for the top-level menu that showcases a list of additional plugins.
     187     *
     188     * @return void
     189     */
     190    public function create_top_level_menu() {
     191        ?>
     192        <h1 style="padding: 15px;">Check out the rest of our plugins</h1>
     193        <ul style="padding-left: 15px; font-size: larger; line-height: 1.5em; list-style: disc;">
     194            <li>
     195                <a target="_blank" href="https://brightleafdigital.io/asana-gravity-forms/">Asana Integration for Gravity Forms</a>
     196            </li>
     197            <li>
     198                <a target="_blank" href="https://brightleafdigital.io/mass-email-notifications-for-gravity-forms/">Mass Email Notifications for Gravity Forms</a>
     199            </li>
     200            <li>
     201                <a target="_blank" href="https://brightleafdigital.io/turn-gravityview-into-a-kanban-project-board/">Kanban View for Gravity View</a>
     202            </li>
     203            <li>
     204                <a target="_blank" href="https://brightleafdigital.io/recurring-form-submissions-for-gravity-forms/">Recurring Form Submissions for Gravity Forms</a>
     205            </li>
     206            <li>
     207                <a target="_blank" href="https://brightleafdigital.io/global-variables-for-gravity-math/">Global Variables for Gravity Math</a>
     208            </li>
     209            <li>
     210                <a target="_blank" href="https://brightleafdigital.io/folders-4-gravity/">Folders 4 Gravity</a>
     211            </li>
     212            <li>
     213                <a target="_blank" href="https://brightleafdigital.io/gravityops-search/">GravityOps Search</a>
     214            </li>
     215            <li>
     216                <a target="_blank" href="https://wordpress.org/plugins/brightleaf-digital-php-compatibility-scanner/">BLD PHP Compatibility Scanner</a>
     217            </li>
     218        </ul>
     219        <?php
     220    }
     221
     222    /**
     223     * Creates a submenu for the plugin in the WordPress admin dashboard.
     224     */
     225    public function create_sub_menu() {
     226        echo '<h1 style="padding-left: 15px;">GravityOps Search is your infinitely customizable VLOOKUP for Gravity Forms!</h1>
     227        <p style="padding-left: 15px; font-size: large">For more information and plugin documentation, visit our <a href="https://brightleafdigital.io/gravityops-search/" target="_blank">plugin page</a>.</p>';
     228    }
     229
     230    /**
    80231     * Processes the gravops_search shortcode to perform searching and displaying Gravity Forms entries
    81232     * based on specified criteria and attributes.
     
    86237     * @return string|false Formatted search results or false if search fails due to missing attributes or invalid setup.
    87238     */
    88     public function gravops_search( $atts, $content = null ) {
     239    public function gravops_search( $atts, $content = null ) {
    89240        $result = apply_filters( 'gogv_shortcode_process', $content );
    90241        if ( $result !== $content ) {
     
    97248                'search'                   => '',
    98249                'operators'                => '',
    99                 'greater_than'             => false,
    100                 'less_than'                => false,
    101250                'display'                  => '',
    102251                'sort_key'                 => 'id',
     
    225374        }
    226375
    227         // Process greater_than attribute
    228         if ( $atts['greater_than'] ) {
    229             $greater_than = array_map( 'trim', explode( ',', $atts['greater_than'] ) );
    230             if ( count( $greater_than ) >= 2 ) {
    231                 $search_criteria['field_filters'][] = [
    232                     'key'      => intval( $greater_than[0] ),
    233                     'value'    => floatval( $greater_than[1] ),
    234                     'operator' => '>',
    235                 ];
    236             }
    237         }
    238 
    239         // Process less_than attribute
    240         if ( $atts['less_than'] ) {
    241             $less_than = array_map( 'trim', explode( ',', $atts['less_than'] ) );
    242             if ( count( $less_than ) >= 2 ) {
    243                 $search_criteria['field_filters'][] = [
    244                     'key'      => intval( $less_than[0] ),
    245                     'value'    => floatval( $less_than[1] ),
    246                     'operator' => '<',
    247                 ];
    248             }
    249         }
    250 
    251376        $sorting = [
    252377            'key'        => sanitize_text_field( $atts['sort_key'] ),
  • gravityops-search/tags/1.0.1/readme.txt

    r3363746 r3403091  
    1 === GravityOps Search ===
     1=== GravityOps Search - Search and Display Gravity Forms Entries ===
    22Contributors: eitanatbrightleaf
    3 Tags: gravity forms, search, shortcode, entries, form data
    4 Requires at least: 5.0
     3Tags: gravity forms, display form entries, frontend entry search, shortcode results display, form data lookup
     4Requires at least: 6.5
    55Tested up to: 6.8
    6 Requires PHP: 7.4
    7 Stable tag: 1.0.0
    8 License: GPL-2.0+
    9 License URI: https://www.gnu.org/licenses/gpl-2.0.html
     6Requires PHP: 8.0
     7Stable tag: 1.0.1
     8License: GPLv2
     9License URI: https://brightleafdigital.io/gravityops-search/
    1010
    11 A shortcode to search and display Gravity Forms entries based on specified criteria and attributes.
     11Search Gravity Forms entries on the front end and display matching results anywhere. Filter by any field value. Output custom formatted data.
    1212
    1313== Description ==
     14GravityOps Search is a free, powerful shortcode for searching Gravity Forms entries on the front end and displaying the matching results anywhere on your site. Instead of paging through the admin entries screen, you can drop a single shortcode into a page, post, GravityView, or custom template and surface exactly the data you need. It works like an Excel-style lookup for Gravity Forms entries: you define which forms and fields to search, how to compare the values, and what to output for each match.
    1415
    15 GravityOps Search provides a powerful shortcode `[gravops_search]` that allows you to search and display Gravity Forms entries based on custom criteria. This plugin extends the Gravity Forms functionality by enabling frontend display of form entries with advanced filtering, sorting, and formatting options.
     16The core `[gravops_search]` shortcode lets you target one form, several forms, or even all forms at once. You can filter by one field or many, pass in values directly in the shortcode content, and control whether entries must match all conditions or any of them. The same shortcode can handle simple lookups (showing a single field from the latest matching entry) or more complex reporting-style views that combine fields, entry properties, and custom HTML. Because everything is driven by attributes, you stay in full control of which entries are included and how their data appears on the front end.
    1617
    17 = Key Features =
     18Results are rendered through a flexible `display` attribute, which understands both simple comma-separated field lists and advanced custom display strings with placeholders. You can output raw values, mix multiple fields into labeled text, or construct HTML lists, tables, and cards with links, CSS classes, and nested shortcodes. This gives you a fully custom front-end listing of Gravity Forms entries that you can drop into any layout, theme, or builder, without building a custom query or touching PHP.
    1819
    19 * Search Gravity Forms entries using custom criteria
    20 * Display specific fields from entries
    21 * Support for multiple search operators (is, contains, like, in, not in, etc.)
    22 * Advanced sorting options with primary and secondary sort keys
    23 * Customizable output formatting with placeholders
    24 * Support for numeric comparison operators (greater than, less than)
    25 * Unique result filtering
    26 * Default values and fallback content
    27 * Link entries directly to admin view
     20GravityOps Search fully supports Gravity Forms entry properties (such as entry ID, form ID, created-by, and more) alongside regular fields, and it includes options for sorting, limiting, and deduplicating results before they are rendered. You can sort by field values or entry properties, choose ascending, descending, or random ordering, add a secondary sort key, and request unique values only. When no entries match, you can show fallback text or per-field default values, so front-end visitors never see a broken layout or confusing blank output.
    2821
    29 = Shortcode Usage =
     22This plugin is built explicitly for front-end entry search and display. It does not add live search tools to the Gravity Forms admin area and does not replace the Entries screen. Instead, it focuses on one thing and does it well: querying Gravity Forms entries in the background and printing clean, formatted results on the pages your users actually see.
    3023
    31 Basic usage:
    32 `[gravops_search target="1" search="2" display="1,2"]Search Value[/gravops_search]`
     24== Features ==
    3325
    34 Advanced usage with operators:
    35 `[gravops_search target="1" search="2,3" operators="contains,=" display="1,2,3" limit="10" sort_key="date_created" sort_direction="DESC"]Value1|Value2[/gravops_search]`
     26* Front-end search for Gravity Forms entries using a single, flexible shortcode.
     27* Target all forms, a single form, or a comma-separated list of form IDs using the `target` attribute.
     28* Filter entries by a comma-separated list of field IDs or entry properties via the `search` attribute.
     29* Pass search values in the shortcode content, separated by a pipe (`|`) to match positions with the fields in `search`.
     30* Choose whether entries must match all search conditions (default) or any condition by setting `search_mode=\"any\"`.
     31* Use the `operators` attribute to control how each value is compared to its field, with support for equals, not-equals, partial matches, SQL-style `LIKE`, “in” / “not in” arrays, and numeric comparisons (greater than / less than / greater-or-equal / less-or-equal).
     32* Display one or many fields and properties for each result using the `display` attribute, which supports both simple lists and rich custom templates.
     33* Include entry properties and field values in your output using placeholder formats like `{13}`, `{id}`, `{form_id}`, and `{gos:id}` where appropriate.
     34* Build fully custom HTML output (lists, tables, cards, badges, buttons, links) directly inside the `display` string.
     35* Insert CSS classes and inline markup into the output so results adopt your theme’s design and layout patterns.
     36* Use the `separator` attribute to control how multiple entry results are separated (including HTML separators or no separator at all using `__none__`).
     37* Sort entries using `sort_key`, `sort_direction`, and `sort_is_num`, with optional `secondary_sort_key` and `secondary_sort_direction` for tie-breaking.
     38* Limit the number of results returned with `limit`, including support for `limit=\"all\"` when you need to show every matching entry.
     39* Turn on `unique` to return only unique result values, great for building deduplicated lists such as unique email addresses, user IDs, or other fields.
     40* Search for empty or blank values with the `search_empty` attribute and an empty shortcode content, to find incomplete or missing data.
     41* Provide fallback values when no entries match—or when individual fields are empty—using the `default` attribute.
     42* Add an admin link to each result with the `link` attribute so power users can jump directly from the front end to the entry in the Gravity Forms admin.
     43* Designed to work smoothly alongside GravityView, GravityMath, and other shortcodes that can be nested inside the output.
     44* Compatible with the legacy `gfsearch` snippet approach while offering ongoing updates and a more robust, plugin-based implementation.
    3645
    37 = Supported Attributes =
     46== How It Works ==
    3847
    39 * `target` - Form ID(s) to search (comma-separated)
    40 * `search` - Field ID(s) to search in (comma-separated)
    41 * `operators` - Search operators (=, is, contains, like, in, not in, etc.)
    42 * `display` - Field ID(s) to display (comma-separated)
    43 * `sort_key` - Field to sort by (default: id)
    44 * `sort_direction` - Sort direction: ASC, DESC, or RAND (default: DESC)
    45 * `sort_is_num` - Sort by numeric value (true/false)
    46 * `secondary_sort_key` - Secondary sort key
    47 * `secondary_sort_direction` - Secondary sort direction
    48 * `unique` - Remove duplicate results (true/false)
    49 * `limit` - Number of results to return (default: 1, use "all" for unlimited)
    50 * `search_mode` - Search mode: "all" or "any" (default: all)
    51 * `separator` - Custom separator for multiple values
    52 * `search_empty` - Search for empty values (true/false)
    53 * `default` - Default value when no results found
    54 * `link` - Add admin link to entries (true/false)
     48At its core, GravityOps Search evaluates your shortcode attributes and content to determine which entries to fetch, then formats each matching entry according to the `display` string you provide. The `target` attribute defines which forms to query: pass `0` to search all forms, a single form ID to target one form, or a comma-separated list of IDs for multi-form searches. The `search` attribute specifies the field IDs and entry properties to filter on, and the shortcode content supplies the corresponding values, separated by the pipe (`|`) character in the same order.
     49
     50You can configure the `search_mode` attribute to determine matching logic. The default mode (`all`) requires each entry to satisfy all conditions, while `search_mode=\"any\"` returns entries that meet at least one of the conditions listed. This gives you the flexibility to build both strict, multi-field filters and more permissive, keyword-style searches. If you need to perform a global search across all fields for a given value, you can leave the relevant search ID blank, and the plugin will look for that value anywhere in the entry.
     51
     52Sorting, limiting, and uniqueness are handled after the search conditions are applied. You can specify a `sort_key` (field ID, entry property, or meta key) with `sort_direction` set to `ASC`, `DESC`, or `RAND`. If you are sorting by numeric data, `sort_is_num` ensures values are compared correctly rather than as plain strings. When you need a consistent secondary ordering—such as sorting first by date and then by name—you can use `secondary_sort_key` and `secondary_sort_direction`. Once ordered, the plugin applies the `limit` attribute to control how many entries are actually returned and optionally filters down to unique results based on the full rendered output when `unique` is enabled.
     53
     54Defaults and fallbacks keep your front-end output robust. The `default` attribute can define text to display when no entries are found or when specific fields are empty, and the plugin can handle multiple default values mapped to multiple display fields. The `separator` attribute governs how multiple entries are joined, making it easy to build line-separated lists, HTML `
     55` elements, or table rows. Because each `[gravops_search]` shortcode runs its own live database query, you can place different instances around your site to build different views of the same underlying Gravity Forms data.
     56
     57== Display and Formatting ==
     58
     59The `display` attribute is the heart of how results are shown. In its simplest form, you can pass a comma-separated list of field IDs or entry properties, such as `display=\"13,14,15\"`. For each matching entry, GravityOps Search outputs those values in order, using sensible default separators between fields and entries. This mode is ideal when you simply need to surface raw values: a quick list of email addresses, a set of IDs, or basic single-column output.
     60
     61For more control, `display` supports custom display strings with placeholders. Instead of a list of IDs, you can provide a template like `display=\"Name: {13}, Email: {14}\"`, which will be rendered for each matching entry. Placeholders like `{13}` insert the value of field 13, while placeholders such as `{id}` and `{form_id}` work with entry properties. When you need to reference non-numeric properties or use merge tags in contexts that parse standard tags (such as GravityView content fields, confirmations, or notifications), you can use the special `{gos:id}` syntax. This gives you a consistent way to assemble complex messages, labels, and markup that incorporate both field data and meta data.
     62
     63The `display` attribute also accepts full HTML, including tags, attributes, and CSS classes. You can wrap values in `
     64`, ``, `
     65`, ``, ``, or any other markup to build lists, tables, cards, or media objects. Because the `separator` attribute supports HTML as well, you can structure your markup so that each entry becomes one list item, table row, or card component. This makes it straightforward to integrate entry results into existing sections of your design, matching your theme and layout without a custom PHP query.
     66
     67== Nesting Shortcodes and Advanced Templates ==
     68
     69GravityOps Search supports nesting other shortcodes inside the `display` attribute via a double-curly-brace syntax: `{{ ... }}`. This means you can embed tools like GravityMath, another `gravops_search`, or any other shortcode directly inside the output template for each entry. The outer `[gravops_search]` processes its own placeholders first and then hands the rendered string to the nested shortcodes, allowing you to feed entry values into calculations, secondary lookups, or formatting helpers.
     70
     71When you nest a second `gravops_search` inside the `display` attribute, each shortcode runs its own search and display logic in sequence. The outer shortcode resolves placeholders such as `{13}` and `{gos:id}` in its `display` string, while the nested shortcode uses its own `display` template and attributes. In nested scenarios where you need to reference placeholder values as input to another shortcode or formula, you can use the `gos:id` pattern without braces (for example, `gos:21`) to avoid conflicts with merge-tag parsing. This lets you do things like passing a field value into a GravityMath filter or dynamically controlling filters and IDs inside the nested shortcode configuration.
     72
     73Because nested shortcodes are fully supported and the plugin respects all standard shortcode attributes, you can construct sophisticated, layered outputs without custom PHP. For example, you can build a front-end summary that uses one `[gravops_search]` to list matching entries, another to pull related entries, and a GravityMath shortcode to compute totals—all wrapped in your own HTML structure. GravityOps Search handles placeholder substitution and nested processing order so that each piece of your template receives the data it needs at the right time.
     74
     75== Search Operators and Multi-Input Fields ==
     76
     77The `operators` attribute lets you tell GravityOps Search exactly how to compare each search value against its corresponding field or property. You define a comma-separated list of operators that line up with the IDs in the `search` attribute. Supported operators include equality (`=` or `is`), inequality (`!=`, `isnot`, `is not`), partial matches (`contains`), SQL-style wildcard matches (`like`), membership tests (`in`, `not in`), and numeric comparisons (`gt`, `lt`, `gt=`, `lt=`). If you provide fewer operators than search fields, remaining fields default to exact matches; extra operators beyond the number of fields are ignored. When you omit `operators` entirely, all fields use exact matching by default.
     78
     79For more advanced scenarios, certain operators expect specific value formats. When using `in` or `not in`, for example, you can pass a PHP-style array in the shortcode content—such as `array(\'item one\',\'item two\',\'item three\')`—to test whether the field value appears in that list. This makes it easy to filter entries against multiple acceptable values for a single field without duplicating field IDs. Combined with `search_mode`, you can express a wide range of conditions: from strict multi-field comparisons to flexible multi-value lists and keyword-style filters.
     80
     81Multi-input Gravity Forms fields (like Name, Address, and Checkbox fields) are fully supported, but they behave differently for display versus search. When displaying, using the base field ID in a placeholder (e.g., `{13}`) automatically combines all sub-inputs (such as first name and last name) into a single string separated by spaces. If you need to display a specific sub-input—like first name only—you can use its input ID directly, for example `{13.3}`. When searching, checkboxes are best handled by searching the base field ID so that changes to individual options or dynamic checkboxes do not break the search. Other multi-input fields (like Name and Address) should be searched using their individual input IDs (e.g., `13.3`, `13.6`), as searching by the base ID will not work for those types.
     82
     83== Performance and Access Control ==
     84
     85Every `[gravops_search]` shortcode runs a live database query against Gravity Forms entries, so thoughtful usage is important for both performance and privacy. On the performance side, heavy use of `limit=\"all\"`, many nested shortcodes, and large forms with complex conditions can slow down page loads. To keep pages responsive, it is recommended to set a reasonable `limit` where possible, minimize unnecessary nesting, and consider caching the rendered page output using your preferred caching plugin or server-level caching tools. These simple steps help ensure that even data-heavy views remain fast and reliable.
     86
     87On the access-control side, the shortcode does not enforce any special permission checks by itself. Anyone who can view the page where the shortcode is placed will be able to see whatever Gravity Forms entry data you choose to display, including potentially sensitive information. To protect private or restricted data, you should place the shortcode inside pages or templates that are protected by membership plugins, password protection, role-based visibility, or other gating mechanisms. This keeps the plugin flexible and focused on data retrieval and formatting, while allowing you to decide how and where to expose entry data based on your site’s security model.
     88
     89GravityOps Search is designed to be both powerful and predictable: you define the forms, fields, filters, and display template, and the plugin takes care of querying and rendering. Used thoughtfully, it becomes a core tool for building dynamic, entry-driven front-end experiences on top of Gravity Forms, without custom development or complex integrations.
    5590
    5691== Installation ==
    57 
    58 1. Upload the plugin files to the `/wp-content/plugins/go-search` directory, or install the plugin through the WordPress plugins screen directly.
    59 2. Activate the plugin through the 'Plugins' screen in WordPress.
    60 3. Make sure you have Gravity Forms installed and activated.
    61 4. Use the `[gravops_search]` shortcode in your posts, pages, or widgets.
     921. Install GravityOps Search:
     93   - In WordPress, go to **Plugins → Add New → Upload Plugin**.
     94   - Upload the ZIP file and click **Install Now**.
     95   - After installation, click **Activate**.
     962. Use the shortcode anywhere you need to search entries:
     97   - Edit a **page**, **post**, **widget**, or **template** that accepts shortcodes.
     98   - Insert the `[gravops_search]` shortcode with the attributes you need.
     99   - Save or update the page.
     1003. View the page on the front end:
     101   - Matching Gravity Forms entries will now display according to your shortcode’s filters and layout.
    62102
    63103== Frequently Asked Questions ==
     104= Does this plugin search the Gravity Forms admin entries screen? =
     105No. GravityOps Search does not modify or enhance the admin-side Entries screen in any way. It is designed exclusively for front-end searching: you place a shortcode on a page, post, or view, and the plugin retrieves matching Gravity Forms entries and displays the data exactly as you format it.
    64106
    65 = Does this plugin require Gravity Forms? =
     107= How do I run a search? =
     108Use the `[gravops_search]` shortcode. You specify which forms to target, which field IDs or entry properties to search, which values to match, and how to output the results. The shortcode runs a live query against Gravity Forms entries and prints the matching results anywhere shortcodes are supported.
    66109
    67 Yes, this plugin extends Gravity Forms functionality and requires Gravity Forms to be installed and activated.
     110= Can I search multiple Gravity Forms at once? =
     111Yes. You can target a single form, several forms, or all forms. Just pass a comma-separated list of form IDs in the `target` attribute, or use `target=\"0\"` to query every form on the site. This allows you to build global lookups and multi-form reporting views.
    68112
    69 = Can I search multiple forms at once? =
     113= Can I filter by more than one field or property? =
     114Yes. The `search` attribute accepts a comma-separated list of field IDs or entry properties. The shortcode content (inside the opening and closing tags) supplies the values, separated with a pipe (`|`) in the same order. You can match on a single field, several fields together, or a mix of fields and entry meta.
    70115
    71 Yes, you can specify multiple form IDs in the `target` attribute separated by commas.
     116= How does the plugin compare values? =
     117Use the `operators` attribute to define comparison behavior for each field. Supported operators include exact match, not-equal, contains, wildcard-style “like”, numeric comparisons (greater-than / less-than), and array-based “in” or “not in” checks. When no operator is provided for a field, the default behavior is exact matching.
    72118
    73 = What search operators are supported? =
     119= Can I return entries that match any of the conditions instead of all? =
     120Yes. By default, the shortcode requires entries to match all conditions. Set `search_mode=\"any\"` to return entries that satisfy at least one of the provided search fields and values.
    74121
    75 The plugin supports: =, is, is not, isnot, !=, contains, like, not in, notin, in, lt (less than), gt (greater than), lt=, gt=
     122= How do I control how results are displayed? =
     123Use the `display` attribute. You can provide:
     124- A comma-separated list of field IDs.
     125- A custom template string with placeholders like `{15}` or `{id}`.
     126- Full HTML markup for custom layouts (lists, cards, rows, tables).
     127This gives you complete control over how each entry appears on the front end.
    76128
    77 = Can I display the search results in a custom format? =
     129= Can I include multiple fields, labels, or HTML in the output? =
     130Yes. The display template supports text, HTML tags, attributes, classes, and multiple placeholders. You can mix fields, entry properties, links, labels, or structured markup to build clean, styled results that match your site’s theme.
    78131
    79 Yes, you can use field placeholders in the `display` attribute like `{1}`, `{gos:2}`, or `{gos:3;default-value}` for custom formatting.
     132= Can I nest other shortcodes inside the display template? =
     133Yes. The plugin supports nested shortcodes using `{{ ... }}` syntax to avoid parsing conflicts. You can nest GravityMath, additional `[gravops_search]` shortcodes, or any shortcode that produces text or numbers. Nested shortcodes receive processed values, enabling chained lookups and computed displays.
    80134
    81 = How do I limit the number of results? =
     135= Does the plugin support multi-input fields like Name, Address, and Checkboxes? =
     136Yes. Multi-input fields can be displayed as either:
     137- Combined values using the base field ID (`{13}`), or
     138- Individual inputs using dot notation (`{13.3}`).
     139For searching, checkboxes should be matched using the base field ID, while other multi-input fields should be matched using specific input IDs.
    82140
    83 Use the `limit` attribute. Set it to a number for a specific limit, or "all" for unlimited results.
     141= Can I search for empty or missing values? =
     142Yes. You can search for empty fields using `search_empty=\"true\"` and passing an empty value for that position in the shortcode content. This is useful for finding incomplete submissions or missing data.
     143
     144= Can I control the order of results? =
     145Yes. Use `sort_key`, `sort_direction`, and optionally `sort_is_num` to sort by numeric or text values. You can also add `secondary_sort_key` and `secondary_sort_direction` for tie-breaking. For random ordering, use `sort_direction=\"RAND\"`.
     146
     147= Can I limit how many entries are returned? =
     148Yes. Use the `limit` attribute. You can return a specific number or use `limit=\"all\"` to show all matches. When combined with sorting, this allows you to show the newest, oldest, largest, smallest, or otherwise top-ranked results.
     149
     150= Can I display only unique values? =
     151Yes. Setting `unique=\"true\"` returns only unique results after formatting. This is ideal for building deduplicated lists such as unique emails, product IDs, or user identifiers pulled from multiple entries.
     152
     153= What happens if no entries match the search? =
     154You can provide fallback text using the `default` attribute. This text displays instead of an empty result, keeping your front-end layout informative and user-friendly.
     155
     156= Does the plugin protect sensitive data? =
     157The plugin displays whatever data you ask it to display. If you include fields with personal or private information, anyone who can access the page will see that data. To restrict visibility, place the shortcode inside protected pages controlled by your membership or role-based access tools.
     158
     159= Will this plugin slow down my site? =
     160Each shortcode triggers a live database query. Normal usage is fast, but heavy configurations—large multi-form searches, deep nesting, or unlimited results—may impact performance. Use reasonable limits where possible and consider caching the page output if you’re displaying large data sets.
     161
     162= Is this compatible with GravityView, GravityMath, and similar tools? =
     163Yes. You can use the shortcode inside GravityView fields, calculations, template blocks, or custom layouts. Nested shortcode support lets you combine data filters, math, and dynamic rendering cleanly.
     164
     165= Can I still use the old gfsearch snippet? =
     166Yes. GravityOps Search supports environments where the old `gfsearch` snippet is present. You can continue using it for legacy shortcodes while using `[gravops_search]` for new builds. They can run simultaneously without conflict.
     167
     168= Do I need to write PHP or custom code? =
     169No. The entire search, filtering, and output process is achieved through shortcode attributes. You can build simple or highly advanced data displays without writing any PHP.
     170
     171== Screenshots ==
     1721. Shows a basic `[gravops_search]` shortcode with search fields and a simple display.
     1732. Displays the formatted results returned by the sample shortcode on a live page.
     1743. Shows a more complex shortcode producing a richer, multi-field front-end layout.
     175
     176== Changelog ==
     177
     178### 1.0.1 | Nov 26, 2025
     179Updating plugin readme and display name.
     180
     181### 1.0.0
     182Initial plugin release based on the original Gravity Forms entry search snippet. This version packages the functionality into a dedicated plugin for easier installation, updates, and ongoing development.
     183
     184== Upgrade Notice ==
     185Upgrade now to get the full plugin version of the original search snippet, with improved stability, easier shortcode usage, and ongoing updates for better front-end entry searching and display.
  • gravityops-search/trunk/gravityops-search.php

    r3363746 r3403091  
    33 * Plugin Name: GravityOps Search
    44 * Description: A shortcode to search and display Gravity Forms entries based on specified criteria and attributes.
    5  * Version: 1.0.0
     5 * Version: 1.0.1
    66 * Author: BrightLeaf Digital
    7  * Author URI: https://digital.brightleaf.info/
     7 * Author URI: https://brightleafdigital.io/
     8 * Plugin URI: https://brightleafdigital.io/gravityops-search/
    89 * License: GPL-2.0+
    910 */
  • gravityops-search/trunk/includes/class-gravityops-search.php

    r3363746 r3403091  
    2525     * @var string Version of the plugin.
    2626     */
    27     protected $_version = '1.0.0';
     27    protected $_version = '1.0.1';
    2828
    2929    /**
     
    3232     * @var string Plugin slug.
    3333     */
    34     protected $_slug = 'gravops_search';
     34    protected $_slug = 'gravityops_search';
    3535
    3636    /**
     
    5353     * @var string Short version of the plugin title to be used in menus.
    5454     */
    55     protected $_short_title = 'GravOps Search';
     55    protected $_short_title = 'GravityOps Search';
    5656    // phpcs:enable PSR2.Classes.PropertyDeclaration.Underscore
    5757
     
    7777    }
    7878
    79     /**
     79    /**
     80     * Initializes the admin functionalities for the application.
     81     *
     82     * Sets up the necessary hooks and actions to configure the admin area, including adding the top-level menu.
     83     *
     84     * @return void This method does not return any value.
     85     */
     86    public function init_admin() {
     87        parent::init_admin();
     88        add_action( 'admin_menu', [ $this, 'add_top_level_menu' ] );
     89    }
     90
     91    /**
     92     * Add a top-level menu in the WordPress admin.
     93     *
     94     * @return void
     95     */
     96    public function add_top_level_menu() {
     97        global $menu;
     98
     99        $has_full_access = current_user_can( 'gform_full_access' );
     100        $min_cap         = GFCommon::current_user_can_which( $this->_capabilities_app_menu );
     101        if ( empty( $min_cap ) ) {
     102            $min_cap = 'gform_full_access';
     103        }
     104
     105        // if another plugin in our suit is already installed and created the submenu we don't have to.
     106        if ( in_array( 'gravity_ops', array_column( $menu, 2 ), true ) ) {
     107            add_submenu_page(
     108                'gravity_ops',
     109                $this->_short_title,
     110                $this->_short_title,
     111                $has_full_access ? 'gform_full_access' : $min_cap,
     112                $this->_slug,
     113                [ $this, 'create_sub_menu' ]
     114            );
     115
     116            return;
     117        }
     118
     119        $number        = 10;
     120        $menu_position = '16.' . $number;
     121        while ( isset( $menu[ $menu_position ] ) ) {
     122            $number       += 10;
     123            $menu_position = '16.' . $number;
     124        }
     125
     126        $this->app_hook_suffix = add_menu_page(
     127            'GravityOps',
     128            'GravityOps',
     129            $has_full_access ? 'gform_full_access' : $min_cap,
     130            'gravity_ops',
     131            [ $this, 'create_top_level_menu' ],
     132            $this->get_app_menu_icon(),
     133            $menu_position
     134        );
     135        add_submenu_page(
     136            'gravity_ops',
     137            $this->_short_title,
     138            $this->_short_title,
     139            $has_full_access ? 'gform_full_access' : $min_cap,
     140            $this->_slug,
     141            [
     142                $this,
     143                'create_sub_menu',
     144            ]
     145        );
     146    }
     147
     148    /**
     149     * Retrieves the SVG icon for the application menu in a base64-encoded string.
     150     *
     151     * The method generates an SVG icon XML, encodes it in base64, and formats it as a data URL
     152     * suitable for use as an image source in web applications.
     153     *
     154     * @return string The base64-encoded SVG icon as a data URL.
     155     */
     156    public function get_app_menu_icon() {
     157        $svg_xml = '<?xml version="1.0" encoding="utf-8"?><svg height="24" id="Layer_1" viewBox="0 0 300 300" width="24" xmlns="http://www.w3.org/2000/svg" >
     158<defs>
     159<style>
     160      .cls-1 {
     161        fill: #fff;
     162      }
     163      .cls-4 {
     164        fill: #fff;
     165      }
     166    </style>
     167<radialGradient cx="-28.79" cy="-50.67" fx="-28.79" fy="-50.67" gradientTransform="translate(.26 .38) scale(1.05)" gradientUnits="userSpaceOnUse" id="radial-gradient" r="433.22">
     168<stop offset="0" stop-color="#402a56"/>
     169<stop offset="1" stop-color="#2f2e41"/>
     170</radialGradient>
     171</defs>
     172<g>
     173<g>
     174<path class="cls-4" d="M204.44,45.16c-7.84,2.35-15.26,5.96-22.05,10.2,0,0-.02,0-.03.01-15.43,9.64-27.63,22.58-34.25,31.59-9.53,13-27.14,30.42-43.32,13.65-2.65-2.75-4.19-6.14-4.72-9.87-1.88-13.02,8.47-30.17,26.39-38.44,33.79-15.6,95.3-12.35,77.98-7.15Z" fill="black"/>
     175<path class="cls-1" d="M214.25,50.81c-4.41,2.77-11.39,11-16.43,17.33,0,0,0,0-.01,0-1.67,2.09-3.13,3.98-4.21,5.39-11.02,14.34-31.85,47.1-37.9,60.65-8.26,18.49-36.2,49.52-61.36,35.86-.16-.08-.32-.18-.47-.27-.04-.02-.08-.05-.12-.06-25.34-14.5-19.28-50.67,2.72-74.12-8.81,13.47-6.66,25.45.75,32.32,17.55,16.25,36.77,2.62,47.34-13.87,8.15-12.72,17.71-24.76,28.14-34.82,8.38-8.08,23.51-19.35,32.73-24.2,3.09-1.64,7.15-3.25,8.83-4.2Z" fill="black"/>
     176<path class="cls-1" d="M221.42,60.81c-.66,1.3-5.48,10.14-10.42,20.46t0,.01c-3.67,7.67-7.41,16.16-9.58,23-4.32,13.6-16.91,56.93-19.49,64.57-4.83,14.29-11.87,24.53-20.51,31.19-.29.23-.58.44-.88.66-9.4,6.88-20.63,9.65-32.99,8.88-15.67-.98-27.53-10.99-31.65-27.29,2.63,5.35,7.76,9.4,16.05,10.18,17.18,1.61,29.48-5.6,37.79-13.93,2.9-2.9,5.31-5.95,7.27-8.81,7.58-11.05,20.74-47.79,28.81-63.68,15.38-30.3,27.18-36.6,35.61-45.22Z" fill="black"/>
     177<path class="cls-1" d="M223.33,174.26h0c-.01.29-.03.58-.05.87-1.12,21.48-14.24,36.62-31.35,38.34-12.52,1.25-24.18-3-31.41-12.78.29-.21.58-.43.88-.66,3.05,1.98,6.75,3.07,11.19,3.03,22.82-.2,31.59-25.49,32.65-44.19,3.54-62.38,17.03-82.68,18.03-85.08-.29,4.36-4.98,17.58-5.62,30.49-.18,3.55-.23,7-.19,10.35h0c.27,21.03,4.28,38.11,5.6,51.39.28,2.83.36,5.58.27,8.23Z" fill="black"/>
     178<path class="cls-1" d="M241.9,175.78c-7.01,2.69-13.2,2.1-18.62-.65.02-.29.03-.58.05-.86,2.51.46,5.02.16,7.53-.96,11.48-5.11,7.91-25.36,3.03-36.08-4.65-10.23-7.63-25.56-8.77-44.1,5.25,23.34,16.89,31.95,23.93,41.17,6.73,8.81,16.03,32.6-7.15,41.48Z" fill="black"/>
     179</g>
     180</g>
     181</svg>';
     182        return sprintf( 'data:image/svg+xml;base64,%s', base64_encode( $svg_xml ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
     183    }
     184
     185    /**
     186     * Outputs the HTML for the top-level menu that showcases a list of additional plugins.
     187     *
     188     * @return void
     189     */
     190    public function create_top_level_menu() {
     191        ?>
     192        <h1 style="padding: 15px;">Check out the rest of our plugins</h1>
     193        <ul style="padding-left: 15px; font-size: larger; line-height: 1.5em; list-style: disc;">
     194            <li>
     195                <a target="_blank" href="https://brightleafdigital.io/asana-gravity-forms/">Asana Integration for Gravity Forms</a>
     196            </li>
     197            <li>
     198                <a target="_blank" href="https://brightleafdigital.io/mass-email-notifications-for-gravity-forms/">Mass Email Notifications for Gravity Forms</a>
     199            </li>
     200            <li>
     201                <a target="_blank" href="https://brightleafdigital.io/turn-gravityview-into-a-kanban-project-board/">Kanban View for Gravity View</a>
     202            </li>
     203            <li>
     204                <a target="_blank" href="https://brightleafdigital.io/recurring-form-submissions-for-gravity-forms/">Recurring Form Submissions for Gravity Forms</a>
     205            </li>
     206            <li>
     207                <a target="_blank" href="https://brightleafdigital.io/global-variables-for-gravity-math/">Global Variables for Gravity Math</a>
     208            </li>
     209            <li>
     210                <a target="_blank" href="https://brightleafdigital.io/folders-4-gravity/">Folders 4 Gravity</a>
     211            </li>
     212            <li>
     213                <a target="_blank" href="https://brightleafdigital.io/gravityops-search/">GravityOps Search</a>
     214            </li>
     215            <li>
     216                <a target="_blank" href="https://wordpress.org/plugins/brightleaf-digital-php-compatibility-scanner/">BLD PHP Compatibility Scanner</a>
     217            </li>
     218        </ul>
     219        <?php
     220    }
     221
     222    /**
     223     * Creates a submenu for the plugin in the WordPress admin dashboard.
     224     */
     225    public function create_sub_menu() {
     226        echo '<h1 style="padding-left: 15px;">GravityOps Search is your infinitely customizable VLOOKUP for Gravity Forms!</h1>
     227        <p style="padding-left: 15px; font-size: large">For more information and plugin documentation, visit our <a href="https://brightleafdigital.io/gravityops-search/" target="_blank">plugin page</a>.</p>';
     228    }
     229
     230    /**
    80231     * Processes the gravops_search shortcode to perform searching and displaying Gravity Forms entries
    81232     * based on specified criteria and attributes.
     
    86237     * @return string|false Formatted search results or false if search fails due to missing attributes or invalid setup.
    87238     */
    88     public function gravops_search( $atts, $content = null ) {
     239    public function gravops_search( $atts, $content = null ) {
    89240        $result = apply_filters( 'gogv_shortcode_process', $content );
    90241        if ( $result !== $content ) {
     
    97248                'search'                   => '',
    98249                'operators'                => '',
    99                 'greater_than'             => false,
    100                 'less_than'                => false,
    101250                'display'                  => '',
    102251                'sort_key'                 => 'id',
     
    225374        }
    226375
    227         // Process greater_than attribute
    228         if ( $atts['greater_than'] ) {
    229             $greater_than = array_map( 'trim', explode( ',', $atts['greater_than'] ) );
    230             if ( count( $greater_than ) >= 2 ) {
    231                 $search_criteria['field_filters'][] = [
    232                     'key'      => intval( $greater_than[0] ),
    233                     'value'    => floatval( $greater_than[1] ),
    234                     'operator' => '>',
    235                 ];
    236             }
    237         }
    238 
    239         // Process less_than attribute
    240         if ( $atts['less_than'] ) {
    241             $less_than = array_map( 'trim', explode( ',', $atts['less_than'] ) );
    242             if ( count( $less_than ) >= 2 ) {
    243                 $search_criteria['field_filters'][] = [
    244                     'key'      => intval( $less_than[0] ),
    245                     'value'    => floatval( $less_than[1] ),
    246                     'operator' => '<',
    247                 ];
    248             }
    249         }
    250 
    251376        $sorting = [
    252377            'key'        => sanitize_text_field( $atts['sort_key'] ),
  • gravityops-search/trunk/readme.txt

    r3363746 r3403091  
    1 === GravityOps Search ===
     1=== GravityOps Search - Search and Display Gravity Forms Entries ===
    22Contributors: eitanatbrightleaf
    3 Tags: gravity forms, search, shortcode, entries, form data
    4 Requires at least: 5.0
     3Tags: gravity forms, display form entries, frontend entry search, shortcode results display, form data lookup
     4Requires at least: 6.5
    55Tested up to: 6.8
    6 Requires PHP: 7.4
    7 Stable tag: 1.0.0
    8 License: GPL-2.0+
    9 License URI: https://www.gnu.org/licenses/gpl-2.0.html
     6Requires PHP: 8.0
     7Stable tag: 1.0.1
     8License: GPLv2
     9License URI: https://brightleafdigital.io/gravityops-search/
    1010
    11 A shortcode to search and display Gravity Forms entries based on specified criteria and attributes.
     11Search Gravity Forms entries on the front end and display matching results anywhere. Filter by any field value. Output custom formatted data.
    1212
    1313== Description ==
     14GravityOps Search is a free, powerful shortcode for searching Gravity Forms entries on the front end and displaying the matching results anywhere on your site. Instead of paging through the admin entries screen, you can drop a single shortcode into a page, post, GravityView, or custom template and surface exactly the data you need. It works like an Excel-style lookup for Gravity Forms entries: you define which forms and fields to search, how to compare the values, and what to output for each match.
    1415
    15 GravityOps Search provides a powerful shortcode `[gravops_search]` that allows you to search and display Gravity Forms entries based on custom criteria. This plugin extends the Gravity Forms functionality by enabling frontend display of form entries with advanced filtering, sorting, and formatting options.
     16The core `[gravops_search]` shortcode lets you target one form, several forms, or even all forms at once. You can filter by one field or many, pass in values directly in the shortcode content, and control whether entries must match all conditions or any of them. The same shortcode can handle simple lookups (showing a single field from the latest matching entry) or more complex reporting-style views that combine fields, entry properties, and custom HTML. Because everything is driven by attributes, you stay in full control of which entries are included and how their data appears on the front end.
    1617
    17 = Key Features =
     18Results are rendered through a flexible `display` attribute, which understands both simple comma-separated field lists and advanced custom display strings with placeholders. You can output raw values, mix multiple fields into labeled text, or construct HTML lists, tables, and cards with links, CSS classes, and nested shortcodes. This gives you a fully custom front-end listing of Gravity Forms entries that you can drop into any layout, theme, or builder, without building a custom query or touching PHP.
    1819
    19 * Search Gravity Forms entries using custom criteria
    20 * Display specific fields from entries
    21 * Support for multiple search operators (is, contains, like, in, not in, etc.)
    22 * Advanced sorting options with primary and secondary sort keys
    23 * Customizable output formatting with placeholders
    24 * Support for numeric comparison operators (greater than, less than)
    25 * Unique result filtering
    26 * Default values and fallback content
    27 * Link entries directly to admin view
     20GravityOps Search fully supports Gravity Forms entry properties (such as entry ID, form ID, created-by, and more) alongside regular fields, and it includes options for sorting, limiting, and deduplicating results before they are rendered. You can sort by field values or entry properties, choose ascending, descending, or random ordering, add a secondary sort key, and request unique values only. When no entries match, you can show fallback text or per-field default values, so front-end visitors never see a broken layout or confusing blank output.
    2821
    29 = Shortcode Usage =
     22This plugin is built explicitly for front-end entry search and display. It does not add live search tools to the Gravity Forms admin area and does not replace the Entries screen. Instead, it focuses on one thing and does it well: querying Gravity Forms entries in the background and printing clean, formatted results on the pages your users actually see.
    3023
    31 Basic usage:
    32 `[gravops_search target="1" search="2" display="1,2"]Search Value[/gravops_search]`
     24== Features ==
    3325
    34 Advanced usage with operators:
    35 `[gravops_search target="1" search="2,3" operators="contains,=" display="1,2,3" limit="10" sort_key="date_created" sort_direction="DESC"]Value1|Value2[/gravops_search]`
     26* Front-end search for Gravity Forms entries using a single, flexible shortcode.
     27* Target all forms, a single form, or a comma-separated list of form IDs using the `target` attribute.
     28* Filter entries by a comma-separated list of field IDs or entry properties via the `search` attribute.
     29* Pass search values in the shortcode content, separated by a pipe (`|`) to match positions with the fields in `search`.
     30* Choose whether entries must match all search conditions (default) or any condition by setting `search_mode=\"any\"`.
     31* Use the `operators` attribute to control how each value is compared to its field, with support for equals, not-equals, partial matches, SQL-style `LIKE`, “in” / “not in” arrays, and numeric comparisons (greater than / less than / greater-or-equal / less-or-equal).
     32* Display one or many fields and properties for each result using the `display` attribute, which supports both simple lists and rich custom templates.
     33* Include entry properties and field values in your output using placeholder formats like `{13}`, `{id}`, `{form_id}`, and `{gos:id}` where appropriate.
     34* Build fully custom HTML output (lists, tables, cards, badges, buttons, links) directly inside the `display` string.
     35* Insert CSS classes and inline markup into the output so results adopt your theme’s design and layout patterns.
     36* Use the `separator` attribute to control how multiple entry results are separated (including HTML separators or no separator at all using `__none__`).
     37* Sort entries using `sort_key`, `sort_direction`, and `sort_is_num`, with optional `secondary_sort_key` and `secondary_sort_direction` for tie-breaking.
     38* Limit the number of results returned with `limit`, including support for `limit=\"all\"` when you need to show every matching entry.
     39* Turn on `unique` to return only unique result values, great for building deduplicated lists such as unique email addresses, user IDs, or other fields.
     40* Search for empty or blank values with the `search_empty` attribute and an empty shortcode content, to find incomplete or missing data.
     41* Provide fallback values when no entries match—or when individual fields are empty—using the `default` attribute.
     42* Add an admin link to each result with the `link` attribute so power users can jump directly from the front end to the entry in the Gravity Forms admin.
     43* Designed to work smoothly alongside GravityView, GravityMath, and other shortcodes that can be nested inside the output.
     44* Compatible with the legacy `gfsearch` snippet approach while offering ongoing updates and a more robust, plugin-based implementation.
    3645
    37 = Supported Attributes =
     46== How It Works ==
    3847
    39 * `target` - Form ID(s) to search (comma-separated)
    40 * `search` - Field ID(s) to search in (comma-separated)
    41 * `operators` - Search operators (=, is, contains, like, in, not in, etc.)
    42 * `display` - Field ID(s) to display (comma-separated)
    43 * `sort_key` - Field to sort by (default: id)
    44 * `sort_direction` - Sort direction: ASC, DESC, or RAND (default: DESC)
    45 * `sort_is_num` - Sort by numeric value (true/false)
    46 * `secondary_sort_key` - Secondary sort key
    47 * `secondary_sort_direction` - Secondary sort direction
    48 * `unique` - Remove duplicate results (true/false)
    49 * `limit` - Number of results to return (default: 1, use "all" for unlimited)
    50 * `search_mode` - Search mode: "all" or "any" (default: all)
    51 * `separator` - Custom separator for multiple values
    52 * `search_empty` - Search for empty values (true/false)
    53 * `default` - Default value when no results found
    54 * `link` - Add admin link to entries (true/false)
     48At its core, GravityOps Search evaluates your shortcode attributes and content to determine which entries to fetch, then formats each matching entry according to the `display` string you provide. The `target` attribute defines which forms to query: pass `0` to search all forms, a single form ID to target one form, or a comma-separated list of IDs for multi-form searches. The `search` attribute specifies the field IDs and entry properties to filter on, and the shortcode content supplies the corresponding values, separated by the pipe (`|`) character in the same order.
     49
     50You can configure the `search_mode` attribute to determine matching logic. The default mode (`all`) requires each entry to satisfy all conditions, while `search_mode=\"any\"` returns entries that meet at least one of the conditions listed. This gives you the flexibility to build both strict, multi-field filters and more permissive, keyword-style searches. If you need to perform a global search across all fields for a given value, you can leave the relevant search ID blank, and the plugin will look for that value anywhere in the entry.
     51
     52Sorting, limiting, and uniqueness are handled after the search conditions are applied. You can specify a `sort_key` (field ID, entry property, or meta key) with `sort_direction` set to `ASC`, `DESC`, or `RAND`. If you are sorting by numeric data, `sort_is_num` ensures values are compared correctly rather than as plain strings. When you need a consistent secondary ordering—such as sorting first by date and then by name—you can use `secondary_sort_key` and `secondary_sort_direction`. Once ordered, the plugin applies the `limit` attribute to control how many entries are actually returned and optionally filters down to unique results based on the full rendered output when `unique` is enabled.
     53
     54Defaults and fallbacks keep your front-end output robust. The `default` attribute can define text to display when no entries are found or when specific fields are empty, and the plugin can handle multiple default values mapped to multiple display fields. The `separator` attribute governs how multiple entries are joined, making it easy to build line-separated lists, HTML `
     55` elements, or table rows. Because each `[gravops_search]` shortcode runs its own live database query, you can place different instances around your site to build different views of the same underlying Gravity Forms data.
     56
     57== Display and Formatting ==
     58
     59The `display` attribute is the heart of how results are shown. In its simplest form, you can pass a comma-separated list of field IDs or entry properties, such as `display=\"13,14,15\"`. For each matching entry, GravityOps Search outputs those values in order, using sensible default separators between fields and entries. This mode is ideal when you simply need to surface raw values: a quick list of email addresses, a set of IDs, or basic single-column output.
     60
     61For more control, `display` supports custom display strings with placeholders. Instead of a list of IDs, you can provide a template like `display=\"Name: {13}, Email: {14}\"`, which will be rendered for each matching entry. Placeholders like `{13}` insert the value of field 13, while placeholders such as `{id}` and `{form_id}` work with entry properties. When you need to reference non-numeric properties or use merge tags in contexts that parse standard tags (such as GravityView content fields, confirmations, or notifications), you can use the special `{gos:id}` syntax. This gives you a consistent way to assemble complex messages, labels, and markup that incorporate both field data and meta data.
     62
     63The `display` attribute also accepts full HTML, including tags, attributes, and CSS classes. You can wrap values in `
     64`, ``, `
     65`, ``, ``, or any other markup to build lists, tables, cards, or media objects. Because the `separator` attribute supports HTML as well, you can structure your markup so that each entry becomes one list item, table row, or card component. This makes it straightforward to integrate entry results into existing sections of your design, matching your theme and layout without a custom PHP query.
     66
     67== Nesting Shortcodes and Advanced Templates ==
     68
     69GravityOps Search supports nesting other shortcodes inside the `display` attribute via a double-curly-brace syntax: `{{ ... }}`. This means you can embed tools like GravityMath, another `gravops_search`, or any other shortcode directly inside the output template for each entry. The outer `[gravops_search]` processes its own placeholders first and then hands the rendered string to the nested shortcodes, allowing you to feed entry values into calculations, secondary lookups, or formatting helpers.
     70
     71When you nest a second `gravops_search` inside the `display` attribute, each shortcode runs its own search and display logic in sequence. The outer shortcode resolves placeholders such as `{13}` and `{gos:id}` in its `display` string, while the nested shortcode uses its own `display` template and attributes. In nested scenarios where you need to reference placeholder values as input to another shortcode or formula, you can use the `gos:id` pattern without braces (for example, `gos:21`) to avoid conflicts with merge-tag parsing. This lets you do things like passing a field value into a GravityMath filter or dynamically controlling filters and IDs inside the nested shortcode configuration.
     72
     73Because nested shortcodes are fully supported and the plugin respects all standard shortcode attributes, you can construct sophisticated, layered outputs without custom PHP. For example, you can build a front-end summary that uses one `[gravops_search]` to list matching entries, another to pull related entries, and a GravityMath shortcode to compute totals—all wrapped in your own HTML structure. GravityOps Search handles placeholder substitution and nested processing order so that each piece of your template receives the data it needs at the right time.
     74
     75== Search Operators and Multi-Input Fields ==
     76
     77The `operators` attribute lets you tell GravityOps Search exactly how to compare each search value against its corresponding field or property. You define a comma-separated list of operators that line up with the IDs in the `search` attribute. Supported operators include equality (`=` or `is`), inequality (`!=`, `isnot`, `is not`), partial matches (`contains`), SQL-style wildcard matches (`like`), membership tests (`in`, `not in`), and numeric comparisons (`gt`, `lt`, `gt=`, `lt=`). If you provide fewer operators than search fields, remaining fields default to exact matches; extra operators beyond the number of fields are ignored. When you omit `operators` entirely, all fields use exact matching by default.
     78
     79For more advanced scenarios, certain operators expect specific value formats. When using `in` or `not in`, for example, you can pass a PHP-style array in the shortcode content—such as `array(\'item one\',\'item two\',\'item three\')`—to test whether the field value appears in that list. This makes it easy to filter entries against multiple acceptable values for a single field without duplicating field IDs. Combined with `search_mode`, you can express a wide range of conditions: from strict multi-field comparisons to flexible multi-value lists and keyword-style filters.
     80
     81Multi-input Gravity Forms fields (like Name, Address, and Checkbox fields) are fully supported, but they behave differently for display versus search. When displaying, using the base field ID in a placeholder (e.g., `{13}`) automatically combines all sub-inputs (such as first name and last name) into a single string separated by spaces. If you need to display a specific sub-input—like first name only—you can use its input ID directly, for example `{13.3}`. When searching, checkboxes are best handled by searching the base field ID so that changes to individual options or dynamic checkboxes do not break the search. Other multi-input fields (like Name and Address) should be searched using their individual input IDs (e.g., `13.3`, `13.6`), as searching by the base ID will not work for those types.
     82
     83== Performance and Access Control ==
     84
     85Every `[gravops_search]` shortcode runs a live database query against Gravity Forms entries, so thoughtful usage is important for both performance and privacy. On the performance side, heavy use of `limit=\"all\"`, many nested shortcodes, and large forms with complex conditions can slow down page loads. To keep pages responsive, it is recommended to set a reasonable `limit` where possible, minimize unnecessary nesting, and consider caching the rendered page output using your preferred caching plugin or server-level caching tools. These simple steps help ensure that even data-heavy views remain fast and reliable.
     86
     87On the access-control side, the shortcode does not enforce any special permission checks by itself. Anyone who can view the page where the shortcode is placed will be able to see whatever Gravity Forms entry data you choose to display, including potentially sensitive information. To protect private or restricted data, you should place the shortcode inside pages or templates that are protected by membership plugins, password protection, role-based visibility, or other gating mechanisms. This keeps the plugin flexible and focused on data retrieval and formatting, while allowing you to decide how and where to expose entry data based on your site’s security model.
     88
     89GravityOps Search is designed to be both powerful and predictable: you define the forms, fields, filters, and display template, and the plugin takes care of querying and rendering. Used thoughtfully, it becomes a core tool for building dynamic, entry-driven front-end experiences on top of Gravity Forms, without custom development or complex integrations.
    5590
    5691== Installation ==
    57 
    58 1. Upload the plugin files to the `/wp-content/plugins/go-search` directory, or install the plugin through the WordPress plugins screen directly.
    59 2. Activate the plugin through the 'Plugins' screen in WordPress.
    60 3. Make sure you have Gravity Forms installed and activated.
    61 4. Use the `[gravops_search]` shortcode in your posts, pages, or widgets.
     921. Install GravityOps Search:
     93   - In WordPress, go to **Plugins → Add New → Upload Plugin**.
     94   - Upload the ZIP file and click **Install Now**.
     95   - After installation, click **Activate**.
     962. Use the shortcode anywhere you need to search entries:
     97   - Edit a **page**, **post**, **widget**, or **template** that accepts shortcodes.
     98   - Insert the `[gravops_search]` shortcode with the attributes you need.
     99   - Save or update the page.
     1003. View the page on the front end:
     101   - Matching Gravity Forms entries will now display according to your shortcode’s filters and layout.
    62102
    63103== Frequently Asked Questions ==
     104= Does this plugin search the Gravity Forms admin entries screen? =
     105No. GravityOps Search does not modify or enhance the admin-side Entries screen in any way. It is designed exclusively for front-end searching: you place a shortcode on a page, post, or view, and the plugin retrieves matching Gravity Forms entries and displays the data exactly as you format it.
    64106
    65 = Does this plugin require Gravity Forms? =
     107= How do I run a search? =
     108Use the `[gravops_search]` shortcode. You specify which forms to target, which field IDs or entry properties to search, which values to match, and how to output the results. The shortcode runs a live query against Gravity Forms entries and prints the matching results anywhere shortcodes are supported.
    66109
    67 Yes, this plugin extends Gravity Forms functionality and requires Gravity Forms to be installed and activated.
     110= Can I search multiple Gravity Forms at once? =
     111Yes. You can target a single form, several forms, or all forms. Just pass a comma-separated list of form IDs in the `target` attribute, or use `target=\"0\"` to query every form on the site. This allows you to build global lookups and multi-form reporting views.
    68112
    69 = Can I search multiple forms at once? =
     113= Can I filter by more than one field or property? =
     114Yes. The `search` attribute accepts a comma-separated list of field IDs or entry properties. The shortcode content (inside the opening and closing tags) supplies the values, separated with a pipe (`|`) in the same order. You can match on a single field, several fields together, or a mix of fields and entry meta.
    70115
    71 Yes, you can specify multiple form IDs in the `target` attribute separated by commas.
     116= How does the plugin compare values? =
     117Use the `operators` attribute to define comparison behavior for each field. Supported operators include exact match, not-equal, contains, wildcard-style “like”, numeric comparisons (greater-than / less-than), and array-based “in” or “not in” checks. When no operator is provided for a field, the default behavior is exact matching.
    72118
    73 = What search operators are supported? =
     119= Can I return entries that match any of the conditions instead of all? =
     120Yes. By default, the shortcode requires entries to match all conditions. Set `search_mode=\"any\"` to return entries that satisfy at least one of the provided search fields and values.
    74121
    75 The plugin supports: =, is, is not, isnot, !=, contains, like, not in, notin, in, lt (less than), gt (greater than), lt=, gt=
     122= How do I control how results are displayed? =
     123Use the `display` attribute. You can provide:
     124- A comma-separated list of field IDs.
     125- A custom template string with placeholders like `{15}` or `{id}`.
     126- Full HTML markup for custom layouts (lists, cards, rows, tables).
     127This gives you complete control over how each entry appears on the front end.
    76128
    77 = Can I display the search results in a custom format? =
     129= Can I include multiple fields, labels, or HTML in the output? =
     130Yes. The display template supports text, HTML tags, attributes, classes, and multiple placeholders. You can mix fields, entry properties, links, labels, or structured markup to build clean, styled results that match your site’s theme.
    78131
    79 Yes, you can use field placeholders in the `display` attribute like `{1}`, `{gos:2}`, or `{gos:3;default-value}` for custom formatting.
     132= Can I nest other shortcodes inside the display template? =
     133Yes. The plugin supports nested shortcodes using `{{ ... }}` syntax to avoid parsing conflicts. You can nest GravityMath, additional `[gravops_search]` shortcodes, or any shortcode that produces text or numbers. Nested shortcodes receive processed values, enabling chained lookups and computed displays.
    80134
    81 = How do I limit the number of results? =
     135= Does the plugin support multi-input fields like Name, Address, and Checkboxes? =
     136Yes. Multi-input fields can be displayed as either:
     137- Combined values using the base field ID (`{13}`), or
     138- Individual inputs using dot notation (`{13.3}`).
     139For searching, checkboxes should be matched using the base field ID, while other multi-input fields should be matched using specific input IDs.
    82140
    83 Use the `limit` attribute. Set it to a number for a specific limit, or "all" for unlimited results.
     141= Can I search for empty or missing values? =
     142Yes. You can search for empty fields using `search_empty=\"true\"` and passing an empty value for that position in the shortcode content. This is useful for finding incomplete submissions or missing data.
     143
     144= Can I control the order of results? =
     145Yes. Use `sort_key`, `sort_direction`, and optionally `sort_is_num` to sort by numeric or text values. You can also add `secondary_sort_key` and `secondary_sort_direction` for tie-breaking. For random ordering, use `sort_direction=\"RAND\"`.
     146
     147= Can I limit how many entries are returned? =
     148Yes. Use the `limit` attribute. You can return a specific number or use `limit=\"all\"` to show all matches. When combined with sorting, this allows you to show the newest, oldest, largest, smallest, or otherwise top-ranked results.
     149
     150= Can I display only unique values? =
     151Yes. Setting `unique=\"true\"` returns only unique results after formatting. This is ideal for building deduplicated lists such as unique emails, product IDs, or user identifiers pulled from multiple entries.
     152
     153= What happens if no entries match the search? =
     154You can provide fallback text using the `default` attribute. This text displays instead of an empty result, keeping your front-end layout informative and user-friendly.
     155
     156= Does the plugin protect sensitive data? =
     157The plugin displays whatever data you ask it to display. If you include fields with personal or private information, anyone who can access the page will see that data. To restrict visibility, place the shortcode inside protected pages controlled by your membership or role-based access tools.
     158
     159= Will this plugin slow down my site? =
     160Each shortcode triggers a live database query. Normal usage is fast, but heavy configurations—large multi-form searches, deep nesting, or unlimited results—may impact performance. Use reasonable limits where possible and consider caching the page output if you’re displaying large data sets.
     161
     162= Is this compatible with GravityView, GravityMath, and similar tools? =
     163Yes. You can use the shortcode inside GravityView fields, calculations, template blocks, or custom layouts. Nested shortcode support lets you combine data filters, math, and dynamic rendering cleanly.
     164
     165= Can I still use the old gfsearch snippet? =
     166Yes. GravityOps Search supports environments where the old `gfsearch` snippet is present. You can continue using it for legacy shortcodes while using `[gravops_search]` for new builds. They can run simultaneously without conflict.
     167
     168= Do I need to write PHP or custom code? =
     169No. The entire search, filtering, and output process is achieved through shortcode attributes. You can build simple or highly advanced data displays without writing any PHP.
     170
     171== Screenshots ==
     1721. Shows a basic `[gravops_search]` shortcode with search fields and a simple display.
     1732. Displays the formatted results returned by the sample shortcode on a live page.
     1743. Shows a more complex shortcode producing a richer, multi-field front-end layout.
     175
     176== Changelog ==
     177
     178### 1.0.1 | Nov 26, 2025
     179Updating plugin readme and display name.
     180
     181### 1.0.0
     182Initial plugin release based on the original Gravity Forms entry search snippet. This version packages the functionality into a dedicated plugin for easier installation, updates, and ongoing development.
     183
     184== Upgrade Notice ==
     185Upgrade now to get the full plugin version of the original search snippet, with improved stability, easier shortcode usage, and ongoing updates for better front-end entry searching and display.
Note: See TracChangeset for help on using the changeset viewer.