CGI-Widget-DBI-Search

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN


=item 0.32 - Jan 28, 2018

Remove Encode::Detect dependency, since simply calling decode('utf8', $s) seems more reliable in general

Ensure all ampersands are html-escaped in final html output: & -> &

Reorganize Display::Grid->display_cell() method to be clearer, and wrap cell content in new div and spans
for better styleability

Sort columns by name and strip html in Display::Grid->display_sort_popup(), and fix to run under CGI 4

Fix Grid and Table tests to get rid of indeterminant test code which can intermittently fail

New method prepend_bind_params(), useful for placeholders in SELECT clause, like MATCH (...) AGAINST (?)

Get rid of a width="100%" in table.searchResultsNavBarTable, so this can be styled by the user with CSS

Instead of die()ing from decode() errors in Base->decode_utf8(), simply return input string

Add -css_dataset_container_id and -css_table_row_extra_attr options

Changes  view on Meta::CPAN

UI improvements to default grid display layout: add 1px border around cells,
and prefix each record value with its header column and a colon (:)

Better error logging: show SQL attempted if statement failed to execute

URI escape key/values in -href_extra_vars option when passed as hashref

Add -browse_mode option for use by CGI::Widget::DBI::Browse, to hide sorting
and paging features during tree navigation

In Grid display class: add popup menu on upper right to allow re-sorting
the dataset; and add page navigation links to top and bottom

Changes to base class constructor to allow passing an object of package
CGI::Widget::DBI::.* to be re-blessed as a Search widget (used by Browse)

=item 0.23 - Apr 24, 2008

Minor fixes to support use with Apache::DBI

Refactor unit testing classes for simplicity, and to support subclassing by

lib/CGI/Widget/DBI/Search/Display/Grid.pm  view on Meta::CPAN

        } else {
            $grid_rows[-1] .= $self->{'dataset_cells_html'}->[$i];
        }
    }

    return ($self->{-optional_header}||'')
      . $self->extra_vars_for_form()
      . '<div id="'.($self->{-css_dataset_container_id} || 'searchWidgetContainerId').'">'
      . ($self->{-browse_mode}
           ? $self->display_pager_links(1, 0, 1)
           : '<div align="right">'.$self->translate('Sort by').': '.$self->display_sort_popup().'</div>'.$self->display_pager_links(1, 0))
      . '<table id="'.($self->{-css_grid_id} || 'searchWidgetGridId').'" class="'.($self->{-css_grid_class} || 'searchWidgetGridTable').'">'
        . $self->{q}->Tr([ @grid_rows ])
      . '</table>'
      . ($self->{-browse_mode}
           ? $self->display_pager_links(0, 1, 1)
           : $self->display_pager_links(0, 1))
      . '</div>'
      . ($self->{-optional_footer}||'');
}

=item display_sort_popup()

Returns an HTML popup with possible columns to sort dataset by, whose values are the
full navigation URIs.  An onChange event causes the URI to be loaded, resorting the
dataset.

=cut

sub display_sort_popup {
    my ($self) = @_;
    my $q = $self->{q};

    my @sortable_cols = ref $self->{-sortable_columns} eq 'HASH'
      ? sort { ($self->{-display_columns}->{$a} || $a) cmp ($self->{-display_columns}->{$b} || $b) } keys %{$self->{-sortable_columns}}
      : @{$self->{'sql_table_display_columns'}};
    $self->{'sortable_columns'} ||= [
        map { $self->_column_name($_) } grep { ! $self->{-unsortable_columns}->{$_} } @sortable_cols
    ];
    return $q->popup_menu(
        -name => 'sortby_columns_popup',
        # call unescape version of sortby_column_uri() to convert &amp; back to & because CGI will autoEscape() - we can't have double-escaping
        -values => [ '', map { $self->sortby_column_uri($_, 1) } @{ $self->{'sortable_columns'} } ],
        -labels => {
            '' => '<'.$self->translate('Sort field').'>',
            map {
                my $lbl = $self->{-display_columns}->{$_};
                $lbl =~ s|<[^<>]+>||g;
                $self->sortby_column_uri($_, 1) => $lbl || $_;
            } @{$self->{'sortable_columns'}}
        },

lib/CGI/Widget/DBI/Search/Display/TEST/Grid.pm  view on Meta::CPAN

        [ 'td', 'tr' ],
    );
}

sub test_search__with_a_join
{
    my $self = shift;
    $self->SUPER::test_search__with_a_join;

    $self->assert_display_contains(
        [ 'div', 'align', 'right', 'Sort by', 'sortby_columns_popup', 'Sort field',
          'sortby=widget_no', 'widget_no', 'sortby=name', 'name',
          'sortby=description', 'description', 'sortby=size', 'size',
          'sortby=tool_no', 'tool_no', 'sortby=tool_name', 'tool_name',
          'sortby=type', 'type', ],
        [ 'tr', 'td' ],
        [ 1, 'clock_widget', 'A time keeper widget', 'small', 2, 'wrench', 'hand' ],
        [ 1, 'clock_widget', 'A time keeper widget', 'small', 1, 'hammer', 'hand' ],
        [ 'td', 'tr', 'tr', 'td' ],
        [ 2, 'calendar_widget', 'A date tracker widget', 'medium', 5, 'emacs', 'software' ],
        [ 2, 'calendar_widget', 'A date tracker widget', 'medium', 6, 'apache', 'software' ],

lib/CGI/Widget/DBI/Search/Display/TEST/Grid.pm  view on Meta::CPAN

    );
}

sub test_search__sorting
{
    my $self = shift;
    $self->SUPER::test_search__sorting;

    # only displays most recent sorting
    $self->assert_display_contains(
        [ 'div', 'align', 'right', 'Sort by', 'sortby_columns_popup',
          'sortby=widget_no', 'widget_no', 'sortby=name', 'name',
          'sortby=description', 'description', 'sortby=size', 'size' ],
        [ 'tr', 'td' ],
        [ 4, 'gps_widget', 'A GPS widget', 'medium' ],
        [ 3, 'silly_widget', 'A goofball widget', 'unknown' ],
        [ 'td', 'tr', 'tr', 'td' ],
        [ 2, 'calendar_widget', 'A date tracker widget', 'medium' ],
        [ 1, 'clock_widget', 'A time keeper widget', 'small' ],
        [ 'td', 'tr' ],
    );

lib/CGI/Widget/DBI/Search/Display/TEST/Grid.pm  view on Meta::CPAN

    $self->_setup_test_search__sorting();
    $ws->{q}->param('href_testvar', 'foo');
    $ws->{q}->param('form_hiddenvar', 'bar');
    $ws->{-href_extra_vars} = { href_testvar => undef };
    $ws->{-form_extra_vars} = { form_hiddenvar => undef };
    $ws->search();

    # only displays most recent sorting
    $self->assert_display_contains(
        [ 'input type="hidden" name="form_hiddenvar" value="bar' ],
        [ 'div', 'align', 'right', 'Sort by', 'sortby_columns_popup',
          'sortby=widget_no&amp;href_testvar=foo', 'widget_no', 'sortby=name&amp;href_testvar=foo', 'name',
          'sortby=description&amp;href_testvar=foo', 'description', 'sortby=size&amp;href_testvar=foo', 'size' ],
        [ 'tr', 'td' ],
        [ 1, 'clock_widget', 'A time keeper widget', 'small' ],
        [ 2, 'calendar_widget', 'A date tracker widget', 'medium' ],
        [ 'td', 'tr', 'tr', 'td' ],
        [ 3, 'silly_widget', 'A goofball widget', 'unknown' ],
        [ 4, 'gps_widget', 'A GPS widget', 'medium' ],
        [ 'td', 'tr' ],
    );

lib/CGI/Widget/DBI/Search/Display/TEST/Grid.pm  view on Meta::CPAN

    $ws->{q}->param('href_testvar', 'foo');
    $ws->{q}->param('form_hiddenvar', 'bar');
    $ws->{-href_extra_vars} = { href_testvar => undef };
    $ws->{-form_extra_vars} = { form_hiddenvar => undef };
    $ws->{-action_uri_js_function} = 'myCustomFunc';
    $ws->search();

    # only displays most recent sorting
    $self->assert_display_contains(
        [ 'input type="hidden" name="form_hiddenvar" value="bar' ],
        [ 'div', 'align', 'right', 'Sort by', 'sortby_columns_popup', ],
        [ map { ("\QmyCustomFunc({ &#39;sortby&#39;: &#39;$_&#39;, &#39;href_testvar&#39;: &#39;foo&#39\E", $_) } qw/widget_no name description size/ ],
        [ 'tr', 'td' ],
        [ 1, 'clock_widget', 'A time keeper widget', 'small' ],
        [ 2, 'calendar_widget', 'A date tracker widget', 'medium' ],
        [ 'td', 'tr', 'tr', 'td' ],
        [ 3, 'silly_widget', 'A goofball widget', 'unknown' ],
        [ 4, 'gps_widget', 'A GPS widget', 'medium' ],
        [ 'td', 'tr' ],
    );
}

lib/CGI/Widget/DBI/Search/Display/TEST/Grid.pm  view on Meta::CPAN

    my $self = shift;
    my $ws = $self->{ws};

    $self->_setup_test_search__sorting();
    $ws->{q}->param('sortby', 'widget_no');

    $ws->{-unsortable_columns} = {size => 1};
    $ws->search();

    $self->assert_display_contains(
        [ 'div', 'align', 'right', 'Sort by', 'sortby_columns_popup',
          'sortby=widget_no', 'widget_no', 'sortby=name', 'name',
          'sortby=description', 'description' ],
        [ 1, 'clock_widget', 'A time keeper widget', 'small' ],
        [ 2, 'calendar_widget', 'A date tracker widget', 'medium' ],
        [ 'td', 'tr', 'tr', 'td' ],
        [ 3, 'silly_widget', 'A goofball widget', 'unknown' ],
        [ 4, 'gps_widget', 'A GPS widget', 'medium' ],
        [ 'td', 'tr' ],
    );
    $self->assert_display_does_not_contain([ 'sortby=size', 'size' ]);

lib/CGI/Widget/DBI/Search/Display/TEST/Grid.pm  view on Meta::CPAN

    $self->init_test_object();
    $self->_setup_test_search__sorting();
    $ws = $self->{ws};
    $ws->{q}->param('sortby', 'widget_no');
    $ws->{q}->param('sort_reverse', 1);

    $ws->{-sortable_columns} = {widget_no => 1, name => 1};
    $ws->search();

    $self->assert_display_contains(
        [ 'div align="right', 'Sort by', 'sortby_columns_popup' ],
        [ 'option value="\?sortby=name">name' ],
        [ 'option selected="selected" value="\?sortby=widget_no&amp;sort_reverse=0">widget_no', ],
        [ 4, 'gps_widget', 'A GPS widget', 'medium' ],
        [ 3, 'silly_widget', 'A goofball widget', 'unknown' ],
        [ 'td', 'tr', 'tr', 'td' ],
        [ 2, 'calendar_widget', 'A date tracker widget', 'medium' ],
        [ 1, 'clock_widget', 'A time keeper widget', 'small' ],
        [ 'td', 'tr' ],
    );
    $self->assert_display_does_not_contain([ 'sortby=description', 'description' ]);

lib/CGI/Widget/DBI/Search/Display/TEST/Grid.pm  view on Meta::CPAN


    $self->assert_display_contains(
        [ 'tr', 'td' ],
        [ 1, 'clock_widget', 'A time keeper widget', 'small' ],
        [ 2, 'calendar_widget', 'A date tracker widget', 'medium' ],
        [ 'td', 'tr', 'tr', 'td' ],
        [ 3, 'silly_widget', 'A goofball widget', 'unknown' ],
        [ 'td', 'tr' ],
    );
    $self->assert_display_does_not_contain([ 'At first page', 'At last page' ]);
    $self->assert_display_does_not_contain([ 'Sort by', 'sortby_columns_popup' ]);
    $self->assert_display_does_not_contain([ 'Sort field' ]);
    $self->assert_display_does_not_contain([ 'sortby=widget_no', 'widget_no' ]);
    $self->assert_display_does_not_contain([ 'sortby=name', 'name', ]);
}

sub test_browse_mode_with_paging
{
    my $self = shift;
    my $ws = $self->{ws};
    $ws->{-browse_mode} = 1;

lib/CGI/Widget/DBI/Search/Display/TEST/Grid.pm  view on Meta::CPAN

    $ws->search();

    $self->assert_display_contains(
        [ 'At first page', '2', 'results displayed', '1 - 2', 'of', '4', 'Next &gt', 'Last &gt' ],
        [ 'tr', 'td' ],
        [ 1, 'clock_widget', 'A time keeper widget', 'small' ],
        [ 2, 'calendar_widget', 'A date tracker widget', 'medium' ],
        [ 'td', 'tr' ],
        [ 'At first page', 'Next &gt', 'Last &gt' ],
    );
    $self->assert_display_does_not_contain([ 'Sort by', 'sortby_columns_popup' ]);
    $self->assert_display_does_not_contain([ 'Sort field' ]);
    $self->assert_display_does_not_contain([ 'sortby=widget_no', 'widget_no' ]);
    $self->assert_display_does_not_contain([ 'sortby=name', 'name', ]);


    # reset search
    $self->init_test_object();
    $ws = $self->{ws};
    $ws->{-browse_mode} = 1;
    $ws->{-max_results_per_page} = 2;



( run in 1.153 second using v1.01-cache-2.11-cpan-364913b4093 )