From 14d7441b73d32614aa0e086c5f69ac087b939a48 Mon Sep 17 00:00:00 2001 From: Julien Heyman Date: Tue, 7 Aug 2012 23:59:18 +0200 Subject: Bug 319598: Add support for saved tabular and graphical reports r/a=LpSolit --- Bugzilla/DB/Schema.pm | 17 +++ Bugzilla/Report.pm | 134 ++++++++++++++++++++++ Bugzilla/User.pm | 30 +++++ report.cgi | 50 ++++++++ template/en/default/global/messages.html.tmpl | 9 ++ template/en/default/global/useful-links.html.tmpl | 13 ++- template/en/default/global/user-error.html.tmpl | 8 ++ template/en/default/reports/report.html.tmpl | 41 +++++-- 8 files changed, 289 insertions(+), 13 deletions(-) create mode 100644 Bugzilla/Report.pm diff --git a/Bugzilla/DB/Schema.pm b/Bugzilla/DB/Schema.pm index 156dca378..728176684 100644 --- a/Bugzilla/DB/Schema.pm +++ b/Bugzilla/DB/Schema.pm @@ -1026,6 +1026,23 @@ use constant ABSTRACT_SCHEMA => { ], }, + reports => { + FIELDS => [ + id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, + PRIMARYKEY => 1}, + user_id => {TYPE => 'INT3', NOTNULL => 1, + REFERENCES => {TABLE => 'profiles', + COLUMN => 'userid', + DELETE => 'CASCADE'}}, + name => {TYPE => 'varchar(64)', NOTNULL => 1}, + query => {TYPE => 'LONGTEXT', NOTNULL => 1}, + ], + INDEXES => [ + reports_user_id_idx => {FIELDS => [qw(user_id name)], + TYPE => 'UNIQUE'}, + ], + }, + component_cc => { FIELDS => [ diff --git a/Bugzilla/Report.pm b/Bugzilla/Report.pm new file mode 100644 index 000000000..4c9f33226 --- /dev/null +++ b/Bugzilla/Report.pm @@ -0,0 +1,134 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +use strict; + +package Bugzilla::Report; + +use base qw(Bugzilla::Object); + +use Bugzilla::CGI; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::Util; + +use constant DB_TABLE => 'reports'; + +# Do not track reports saved by users. +use constant AUDIT_CREATES => 0; +use constant AUDIT_UPDATES => 0; +use constant AUDIT_REMOVES => 0; + +use constant DB_COLUMNS => qw( + id + user_id + name + query +); + +use constant UPDATE_COLUMNS => qw( + name + query +); + +use constant VALIDATORS => { + name => \&_check_name, + query => \&_check_query, +}; + +############## +# Validators # +############## + +sub _check_name { + my ($invocant, $name) = @_; + $name = clean_text($name); + $name || ThrowUserError("report_name_missing"); + $name !~ /[<>&]/ || ThrowUserError("illegal_query_name"); + if (length($name) > MAX_LEN_QUERY_NAME) { + ThrowUserError("query_name_too_long"); + } + return $name; +} + +sub _check_query { + my ($invocant, $query) = @_; + $query || ThrowUserError("buglist_parameters_required"); + my $cgi = new Bugzilla::CGI($query); + $cgi->clean_search_url; + return $cgi->query_string; +} + +############# +# Accessors # +############# + +sub query { return $_[0]->{'query'}; } + +sub set_name { $_[0]->set('name', $_[1]); } +sub set_query { $_[0]->set('query', $_[1]); } + +########### +# Methods # +########### + +sub create { + my $class = shift; + my $param = shift; + + Bugzilla->login(LOGIN_REQUIRED); + $param->{'user_id'} = Bugzilla->user->id; + + unshift @_, $param; + my $self = $class->SUPER::create(@_); +} + +sub check { + my $class = shift; + my $report = $class->SUPER::check(@_); + my $user = Bugzilla->user; + if ( grep($_->id eq $report->id, @{$user->reports})) { + return $report; + } else { + ThrowUserError('report_access_denied'); + } +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Report - Bugzilla report class. + +=head1 SYNOPSIS + + use Bugzilla::Report; + + my $report = new Bugzilla::Report(1); + + my $report = Bugzilla::Report->check({id => $id}); + + my $name = $report->name; + my $query = $report->query; + + my $report = Bugzilla::Report->create({ name => $name, query => $query }); + + $report->set_name($new_name); + $report->set_query($new_query); + $report->update(); + + $report->remove_from_db; + +=head1 DESCRIPTION + +Report.pm represents a Report object. It is an implementation +of L, and thus provides all methods that +L provides. + +=cut diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index 9c869dc63..708d12c64 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -578,6 +578,25 @@ sub save_last_search { return $search; } +sub reports { + my $self = shift; + return $self->{reports} if defined $self->{reports}; + return [] unless $self->id; + + my $dbh = Bugzilla->dbh; + my $report_ids = $dbh->selectcol_arrayref( + 'SELECT id FROM reports WHERE user_id = ?', undef, $self->id); + require Bugzilla::Report; + $self->{reports} = Bugzilla::Report->new_from_list($report_ids); + return $self->{reports}; +} + +sub flush_reports_cache { + my $self = shift; + + delete $self->{reports}; +} + sub settings { my ($self) = @_; @@ -2275,6 +2294,17 @@ Should only be called by C, for the most part. Returns the disable text of the user, if any. +=item C + +Returns an arrayref of the user's own saved reports. The array contains +L objects. + +=item C + +Some code modifies the set of stored reports. Because C does +not handle these modifications, but does cache the result of calling C +internally, such code must call this method to flush the cached result. + =item C Returns a hash of hashes which holds the user's settings. The first key is diff --git a/report.cgi b/report.cgi index 83561fde5..5778841b3 100755 --- a/report.cgi +++ b/report.cgi @@ -15,6 +15,8 @@ use Bugzilla::Util; use Bugzilla::Error; use Bugzilla::Field; use Bugzilla::Search; +use Bugzilla::Report; +use Bugzilla::Token; use List::MoreUtils qw(uniq); @@ -34,6 +36,7 @@ if (grep(/^cmd-/, $cgi->param())) { Bugzilla->login(); my $action = $cgi->param('action') || 'menu'; +my $token = $cgi->param('token'); if ($action eq "menu") { # No need to do any searching in this case, so bail out early. @@ -41,6 +44,52 @@ if ($action eq "menu") { $template->process("reports/menu.html.tmpl", $vars) || ThrowTemplateError($template->error()); exit; + +} +elsif ($action eq 'add') { + my $user = Bugzilla->login(LOGIN_REQUIRED); + check_hash_token($token, ['save_report']); + + my $name = clean_text($cgi->param('name')); + my $query = $cgi->param('query'); + + if (my ($report) = grep{ lc($_->name) eq lc($name) } @{$user->reports}) { + $report->set_query($query); + $report->update; + $vars->{'message'} = "report_updated"; + } else { + my $report = Bugzilla::Report->create({name => $name, query => $query}); + $vars->{'message'} = "report_created"; + } + + $user->flush_reports_cache; + + print $cgi->header(); + + $vars->{'reportname'} = $name; + + $template->process("global/message.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; +} +elsif ($action eq 'del') { + my $user = Bugzilla->login(LOGIN_REQUIRED); + my $report_id = $cgi->param('saved_report_id'); + check_hash_token($token, ['delete_report', $report_id]); + + my $report = Bugzilla::Report->check({id => $report_id}); + $report->remove_from_db(); + + $user->flush_reports_cache; + + print $cgi->header(); + + $vars->{'message'} = 'report_deleted'; + $vars->{'reportname'} = $report->name; + + $template->process("global/message.html.tmpl", $vars) + || ThrowTemplateError($template->error()); + exit; } # Sanitize the URL, to make URLs shorter. @@ -209,6 +258,7 @@ $vars->{'width'} = $width if $width; $vars->{'height'} = $height if $height; $vars->{'query'} = $query; +$vars->{'saved_report_id'} = $cgi->param('saved_report_id'); $vars->{'debug'} = $cgi->param('debug'); my $formatparam = $cgi->param('format'); diff --git a/template/en/default/global/messages.html.tmpl b/template/en/default/global/messages.html.tmpl index d39890c13..d93ed537e 100644 --- a/template/en/default/global/messages.html.tmpl +++ b/template/en/default/global/messages.html.tmpl @@ -795,6 +795,15 @@ or you don't have access to it. The following is a list of the products you can choose from. + [% ELSIF message_tag == "report_created" %] + OK, you have a new saved report named [% reportname FILTER html %]. + + [% ELSIF message_tag == "report_deleted" %] + OK, the [% reportname FILTER html %] report is gone. + + [% ELSIF message_tag == "report_updated" %] + The saved report [% reportname FILTER html %] has been updated. + [% ELSIF message_tag == "remaining_time_zeroed" %] The [% field_descs.remaining_time FILTER html %] field has been set to zero automatically as part of closing this [% terms.bug %] diff --git a/template/en/default/global/useful-links.html.tmpl b/template/en/default/global/useful-links.html.tmpl index 1b5ba9a30..5959ab656 100644 --- a/template/en/default/global/useful-links.html.tmpl +++ b/template/en/default/global/useful-links.html.tmpl @@ -56,7 +56,18 @@ [% END %] - [%# Individual bugs addition %] + [% IF user.reports.size %] +
  • + +
  • + [% END %] [%# Sections of links to more things users can do on this installation. %] [% Hook.process("end") %] diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index 0d51eaa1b..fbb9ca169 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -1480,6 +1480,14 @@ To reassign [% terms.abug %], you must provide an address for the new assignee. + [% ELSIF error == "report_name_missing" %] + [% title = "No Report Name Specified" %] + You must enter a name for your report. + + [% ELSIF error == "report_access_denied" %] + [% title = "Report Access Denied" %] + You cannot access this report. + [% ELSIF error == "require_component" %] [% title = "Component Needed" %] To file this [% terms.bug %], you must first choose a component. diff --git a/template/en/default/reports/report.html.tmpl b/template/en/default/reports/report.html.tmpl index 84cefbded..76049d04e 100644 --- a/template/en/default/reports/report.html.tmpl +++ b/template/en/default/reports/report.html.tmpl @@ -150,18 +150,35 @@ -

    - [% IF format == "table" %] - Edit - this report - [% ELSE %] - - Edit this report - - [% END %] -

    - + + + + + + +
    + [% IF format == "table" %] + Edit this report + [% ELSE %] + + Edit this report + [% END %] +   + [% IF saved_report_id %] + Forget this report + [% ELSE %] +
    + as + + + + +
    + [% END %] +
    + [% PROCESS global/footer.html.tmpl %] -- cgit v1.2.3-24-g4f1b