From 9cb01b1aed97bcbe897e779dc268800a2b1538ae Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Thu, 8 Sep 2016 11:25:59 -0700 Subject: [PATCH] add UI to manage saved searches, #72101 --- httemplate/browse/saved_search.html | 81 +++++++++++++++++++++++++ httemplate/edit/process/saved_search.html | 15 +++++ httemplate/edit/saved_search.html | 91 +++++++++++++++++++++++++++++ httemplate/elements/menu.html | 17 ++++++ httemplate/elements/tr-fixed-date.html | 2 +- httemplate/misc/delete-saved_search.html | 25 ++++++++ httemplate/search/elements/search-html.html | 24 ++++++-- httemplate/search/elements/search.html | 1 + 8 files changed, 250 insertions(+), 6 deletions(-) create mode 100644 httemplate/browse/saved_search.html create mode 100644 httemplate/edit/process/saved_search.html create mode 100644 httemplate/edit/saved_search.html create mode 100644 httemplate/misc/delete-saved_search.html diff --git a/httemplate/browse/saved_search.html b/httemplate/browse/saved_search.html new file mode 100644 index 000000000..d2efa6ed9 --- /dev/null +++ b/httemplate/browse/saved_search.html @@ -0,0 +1,81 @@ +<& elements/browse.html, + 'title' => 'My saved searches', + 'name' => 'saved searches', + 'query' => { 'table' => 'saved_search', + 'hashref' => { usernum => $curuser->usernum }, + }, + 'count_query' => $count_query, + 'header' => [ '#', + 'Name', + 'Subscription', + 'Last sent', + 'Format', + 'Path', + 'Parameters', + ], + 'sort_fields' => [ 'searchnum', + 'searchname', + 'freq', + 'last_sent', + 'format', + "path || '?' || 'params'", + '', + ], + 'fields' => [ 'searchnum', + 'searchname', + 'freq', + sub { my $date = shift->get('last_sent'); + $date ? time2str('%b %o, %Y', $date) : ''; + }, + sub { $format_label{ shift->get('format') } + }, + 'path', + sub { join('
', + sort + map { encode_entities(uri_unescape($_)) } + split(/[;&]/, shift->get('params') ) + ) + }, + ], + 'size' => [ '', + '', + '', + '', + '', + '', + '-1', + ], + 'links' => [ '', '' ], + 'link_onclicks' => [ '', $edit_popup ], +# 'disableable' => 1, # currrently unused +# 'disabled_statuspos' => 2, + 'really_disable_download' => 1 +&> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +my $query = { + 'table' => 'saved_search', + 'hashref' => { 'usernum' => $curuser->usernum }, +}; +my $count_query = "SELECT COUNT(*) FROM saved_search WHERE usernum = ". + $curuser->usernum; + +my %format_label = ( + 'html' => 'webpage', + 'csv' => 'CSV', + 'xls' => 'spreadsheet', +); + +my $edit_popup = sub { + my $searchnum = shift->searchnum; + include('/elements/popup_link_onclick.html', + 'action' => $fsurl.'/edit/saved_search.html?'.$searchnum, + 'actionlabel' => 'Save this search', + 'width' => 650, + 'height' => 500, + ); +}; + + diff --git a/httemplate/edit/process/saved_search.html b/httemplate/edit/process/saved_search.html new file mode 100644 index 000000000..7ae7e0d78 --- /dev/null +++ b/httemplate/edit/process/saved_search.html @@ -0,0 +1,15 @@ +<& elements/process.html, + 'table' => 'saved_search', + 'popup_reload' => 'Saving', + 'post_new_object_callback' => $callback, +&> +<%init> + +my $callback = sub { + my ($cgi, $obj) = @_; + $obj->usernum( $FS::CurrentUser::CurrentUser->usernum ); + # if this would change it from its existing owner, replace_check + # will refuse +}; + + diff --git a/httemplate/edit/saved_search.html b/httemplate/edit/saved_search.html new file mode 100644 index 000000000..3039aed35 --- /dev/null +++ b/httemplate/edit/saved_search.html @@ -0,0 +1,91 @@ +<& elements/edit.html, + 'name' => 'saved search', + 'table' => 'saved_search', + 'popup' => 1, + 'fields' => [ + { field => 'searchname', + type => 'text', + size => 40, + }, + { field => 'freq', + type => 'select', + options => [ '', 'daily', 'weekly', 'monthly' ], + labels => { '' => 'no' }, + }, + { field => 'emailaddress', + type => 'fixed', + curr_value_callback => sub { + $curuser->option('email_address') + || 'no email address configured' + }, + }, + { field => 'last_sent', + type => 'fixed-date', + }, + { field => 'format', + type => 'hidden', # revisit this later +# type => 'select', +# options => [ 'html', 'xls', 'csv' ], +# labels => { +# 'html' => 'webpage', +# 'xls' => 'spreadsheet', +# 'csv' => 'CSV', +# }, + }, + { field => 'disabled', # currently unused + type => 'hidden', + }, + { type => 'tablebreak-tr-title' }, + { field => 'path', + type => 'fixed', + cell_style => 'font-size: small', + }, + { field => 'params', + type => 'fixed', + cell_style => 'font-size: small', + }, + ], + 'labels' => { + 'searchnum' => 'Saved search', + 'searchname' => 'Name this search', + 'path' => 'Search page', + 'params' => 'Parameters', + 'freq' => 'Subscribe by email', + 'last_sent' => 'Last sent on', + 'emailaddress' => 'Will be sent to', + 'format' => 'Report format', + }, + 'new_object_callback' => $new_object, + 'delete_url' => $fsurl.'misc/delete-saved_search.html', +&> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +# prefix to the freeside document root (usually '/freeside/') +my $root = URI->new($fsurl)->path; + +# alternatively, could do all this on the client using window.top.location +my $new_object = sub { + my $cgi = shift; + my $hashref = shift; + my $fields = shift; + for (grep { $_->{field} eq 'last_sent' } @$fields) { + $_->{type} = 'hidden'; + } + my $url = $r->header_in('Referer') + or die "no referring page found"; + $url = URI->new($url); + my $path = $url->path; + $path =~ s/^$root//; # path should not have a leading slash + my $title = $cgi->param('title'); + return FS::saved_search->new({ + 'usernum' => $curuser->usernum, + 'path' => $path, + 'params' => $url->query, + 'format' => 'html', + 'searchname' => $title, + }); +}; + + diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index cdb1d733c..621165de4 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -87,6 +87,21 @@ my $mobile = $opt{'mobile'} || 0; my $curuser = $FS::CurrentUser::CurrentUser; +# saved searches +tie my %report_saved_searches, 'Tie::IxHash'; +if ( my @searches = grep { $_->disabled eq '' } $curuser->saved_search ) { + foreach my $search (@searches) { + $report_saved_searches{ $search->searchname } = [ + # don't use query_string here; we don't want to override the format + $fsurl . $search->path . '?' . $search->params , '' + ]; + } + $report_saved_searches{'separator'} = ''; + $report_saved_searches{'My saved searches'} = + [ $fsurl. 'browse/saved_search.html', + 'Manage saved searches and subscriptions' ]; +} + #XXX Active tickets not assigned to a customer tie my %report_prospects, 'Tie::IxHash'; @@ -412,6 +427,8 @@ $report_logs{'Outgoing messages'} = [ $fsurl.'search/cust_msg.html', 'View outgo || $curuser->access_right('Configuration'); tie my %report_menu, 'Tie::IxHash'; +$report_menu{'Saved searches'} = [ \%report_saved_searches, 'My saved searches' ] + if keys(%report_saved_searches); $report_menu{'Prospects'} = [ \%report_prospects, 'Prospect reports' ] if $curuser->access_right('List prospects') || $curuser->access_right('List contacts'); diff --git a/httemplate/elements/tr-fixed-date.html b/httemplate/elements/tr-fixed-date.html index ef599796d..731a3caa7 100644 --- a/httemplate/elements/tr-fixed-date.html +++ b/httemplate/elements/tr-fixed-date.html @@ -14,6 +14,6 @@ my $value = $opt{'curr_value'} || $opt{'value'}; my $conf = new FS::Conf; my $date_format = $opt{'format'} || $conf->config('date_format') || '%m/%d/%Y'; -$opt{'formatted_value'} = time2str($date_format, $value); +$opt{'formatted_value'} = $value > 0 ? time2str($date_format, $value) : ''; diff --git a/httemplate/misc/delete-saved_search.html b/httemplate/misc/delete-saved_search.html new file mode 100644 index 000000000..34567ec1c --- /dev/null +++ b/httemplate/misc/delete-saved_search.html @@ -0,0 +1,25 @@ +% if ( $error ) { +<& /elements/errorpage-popup.html, $error &> +% } else { +<& /elements/header-popup.html, 'Saved search deleted' &> + + + +% } +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +my($query) = $cgi->keywords; +$query =~ /^(\d+)$/ || die "Illegal searchnum"; +my $searchnum = $1; + +my $search = qsearchs('saved_search', { + 'searchnum' => $searchnum, + 'usernum' => $curuser->usernum, +}); +my $error = $search->delete; + + diff --git a/httemplate/search/elements/search-html.html b/httemplate/search/elements/search-html.html index 12f6c1e04..3ea38aee8 100644 --- a/httemplate/search/elements/search-html.html +++ b/httemplate/search/elements/search-html.html @@ -136,22 +136,36 @@ - <% $opt{'download_label'} || 'Download full results' %>
+ <% $opt{'download_label'} || 'Download results:' %> % $cgi->param('_type', "$xlsname.xls" ); - as query_string %>">Excel spreadsheet
+ query_string %>">Spreadsheet |  % $cgi->param('_type', 'csv'); - as query_string %>">CSV file
+ query_string %>">CSV |  % if ( defined($opt{xml_elements}) ) { % $cgi->param('_type', 'xml'); - as query_string %>">XML file
+ query_string %>">XML |  % } % $cgi->param('_type', 'html-print'); - as query_string %>">printable copy + query_string %>">webpage +%# "save search" -- for now, obey disable_download and the 'Download +%# report data' ACL, because saving a search allows the user to receive +%# copies of the data. +
+%# XXX should do a check here on whether the user already has this +%# search saved... + <& /elements/popup_link.html, + 'action' => $fsurl.'/edit/saved_search.html?title='. + uri_escape($opt{title}), + 'label' => 'Save this search', + 'actionlabel' => 'Save this search', + 'width' => 650, + 'height' => 500, + &> % $cgi->param('_type', "html" ); % } diff --git a/httemplate/search/elements/search.html b/httemplate/search/elements/search.html index 8b85324c9..4ef8c25d3 100644 --- a/httemplate/search/elements/search.html +++ b/httemplate/search/elements/search.html @@ -179,6 +179,7 @@ Example: &> +% # if changing this, also update saved search behavior to match! % if ( $type eq 'csv' ) { % <% include('search-csv.html', header=>$header, rows=>$rows, opt=>\%opt ) %> -- 2.11.0