view release on metacpan or search on metacpan
=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
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 & 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&href_testvar=foo', 'widget_no', 'sortby=name&href_testvar=foo', 'name',
'sortby=description&href_testvar=foo', 'description', 'sortby=size&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({ 'sortby': '$_', 'href_testvar': 'foo'\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&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 >', 'Last >' ],
[ 'tr', 'td' ],
[ 1, 'clock_widget', 'A time keeper widget', 'small' ],
[ 2, 'calendar_widget', 'A date tracker widget', 'medium' ],
[ 'td', 'tr' ],
[ 'At first page', 'Next >', 'Last >' ],
);
$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;