import rt 3.8.9
[freeside.git] / rt / share / html / Dashboards / Queries.html
1 %# BEGIN BPS TAGGED BLOCK {{{
2 %#
3 %# COPYRIGHT:
4 %#
5 %# This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
6 %#                                          <sales@bestpractical.com>
7 %#
8 %# (Except where explicitly superseded by other copyright notices)
9 %#
10 %#
11 %# LICENSE:
12 %#
13 %# This work is made available to you under the terms of Version 2 of
14 %# the GNU General Public License. A copy of that license should have
15 %# been provided with this software, but in any event can be snarfed
16 %# from www.gnu.org.
17 %#
18 %# This work is distributed in the hope that it will be useful, but
19 %# WITHOUT ANY WARRANTY; without even the implied warranty of
20 %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 %# General Public License for more details.
22 %#
23 %# You should have received a copy of the GNU General Public License
24 %# along with this program; if not, write to the Free Software
25 %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 %# 02110-1301 or visit their web page on the internet at
27 %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
28 %#
29 %#
30 %# CONTRIBUTION SUBMISSION POLICY:
31 %#
32 %# (The following paragraph is not intended to limit the rights granted
33 %# to you to modify and distribute this software under the terms of
34 %# the GNU General Public License and is only of importance to you if
35 %# you choose to contribute your changes and enhancements to the
36 %# community by submitting them to Best Practical Solutions, LLC.)
37 %#
38 %# By intentionally submitting any modifications, corrections or
39 %# derivatives to this work, or any other work intended for use with
40 %# Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 %# you are the copyright holder for those contributions and you grant
42 %# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
43 %# royalty-free, perpetual, license to use, copy, create derivative
44 %# works based on those contributions, and sublicense and distribute
45 %# those contributions and any derivatives thereof.
46 %#
47 %# END BPS TAGGED BLOCK }}}
48 <& /Elements/Header, Title => $title &>
49 <& /Dashboards/Elements/Tabs,
50     current_subtab => $current_subtab,
51     Title => $title,
52     DashboardObj => $Dashboard &>
53
54 <& /Widgets/SelectionBox:header, nojs => 1 &>
55
56 <& /Elements/ListActions, actions => \@results &>
57
58 <& Elements/Deleted, searches => \@deleted, Dashboard => $Dashboard &>
59
60 <& Elements/HiddenSearches, searches => \@hidden_searches, Dashboard => $Dashboard &>
61
62 <table width="100%" border="0">
63 % for my $pane (@panes) {
64 <tr><td valign="top" class="boxcontainer">
65 <form action="Queries.html" name="Dashboard-<%$pane->{Name}%>" method="post" enctype="multipart/form-data">
66 <input type="hidden" class="hidden" name="id" value="<%$Dashboard->Id%>" />
67 <input type="hidden" class="hidden" name="Privacy" value="<%$Dashboard->Privacy%>" />
68
69 <&| /Widgets/TitleBox, title => $pane->{DisplayName} &>
70 % my ( $pane_name ) = $pane->{Name} =~ /Searches-(.+)/;
71     <& /Widgets/SelectionBox:show, self => $pane, nojs => 1, grep( {
72             $_->{pane} eq $pane_name} @deleted ) ? ( ShowUpdate => 1 ) : () &>
73 </&>
74 </form>
75 </td></tr>
76 % }
77 </table>
78
79 <%INIT>
80
81 my $current_subtab = 'Dashboards/Queries.html?id=' . $id;
82 my @results;
83
84 use RT::Dashboard;
85 my $Dashboard = new RT::Dashboard($session{'CurrentUser'});
86 my ($ok, $msg) = $Dashboard->LoadById($id);
87 $ok || Abort(loc("Couldn't load dashboard [_1]: [_2]", $id, $msg));
88 my $title = loc("Modify the queries of dashboard [_1]", $Dashboard->Name);
89
90 my %desc_of;
91 my @items;
92 my %selected;
93 my %still_exists;
94
95 # add portlets (homepage componenets)
96 my @components = @{ RT->Config->Get('HomepageComponents') };
97
98 for my $desc (@components) {
99     my $name = "component-$desc";
100     push @items, [$name, $desc];
101     $desc_of{$name} = $desc;
102     $still_exists{$name} = 1;
103 }
104
105 # add dashboards
106 my $dashboards = $m->comp("/Dashboards/Elements/DashboardsForObjects", flatten => 1);
107 for my $dashboard (@{ $dashboards || [] }) {
108     # Users *can* set up mutually recursive dashboards, but don't make it THIS
109     # easy for them to shoot themselves in the foot.
110     next if $dashboard->Id == $Dashboard->Id;
111
112     my $name = 'dashboard-' . $dashboard->Id . '-' . $dashboard->Privacy;
113     my $desc = "Dashboard: " . $dashboard->Name;
114     push @items, [$name, $desc];
115     $desc_of{$name} = $desc;
116     $still_exists{$name} = 1;
117 }
118
119 # add saved searches
120 my @objs = RT::System->new($session{'CurrentUser'});
121
122 push @objs, RT::SavedSearches->new( $session{CurrentUser} )->_PrivacyObjects
123     if $session{'CurrentUser'}->HasRight( Right  => 'LoadSavedSearch',
124                                           Object => $RT::System );
125
126 for my $object (@objs) {
127     for ($m->comp("/Search/Elements/SearchesForObject", Object => $object)) {
128         my ($desc, $search) = @$_;
129         my $SearchType = $search->Content->{'SearchType'} || 'Ticket';
130         my $type = ($SearchType eq 'Ticket') ? 'Saved Search' : $SearchType; # loc
131         $desc = "$type: $desc";
132         my $privacy = $Dashboard->_build_privacy($object);
133         my $name = 'search-' . $search->Id . '-' . $privacy;
134         push @items, [$name, $desc];
135         $desc_of{$name} = $desc;
136         $still_exists{$name} = 1;
137     }
138 }
139
140 # Get the list of queries already in use
141 my @deleted;
142 do {
143     my $panes = $Dashboard->Panes;
144     for my $pane (keys %$panes) {
145         for my $portlet (@{ $panes->{$pane} }) {
146             my $name;
147             my $type = $portlet->{portlet_type};
148
149             if ($type eq 'search' || $type eq 'dashboard') {
150                 $name = join '-', $type, $portlet->{id}, $portlet->{privacy};
151             }
152             elsif ($type eq 'component') {
153                 $name = join '-', 'component', $portlet->{component};
154             }
155
156             if (!$still_exists{$name}) {
157                 push @deleted, {
158                     pane => $pane,
159                     name => $name,
160                     description => $portlet->{description},
161                 };
162                 next;
163             }
164
165             push @{ $selected{$pane} }, $name;
166             $desc_of{$name} = $portlet->{description};
167         }
168     }
169 };
170
171 $m->callback(
172     CallbackName => 'PopulatePossibilities',
173     Dashboard    => $Dashboard,
174     items        => \@items,
175     desc_of      => \%desc_of,
176     still_exists => \%still_exists,
177     selected     => \%selected,
178 );
179
180 # Create selectionbox widgets for those queries
181
182 my %pane_name = (
183     'body'    => loc('Body'),
184     'sidebar' => loc('Sidebar'),
185 );
186
187 $m->callback(
188     CallbackName => 'Panes',
189     Dashboard    => $Dashboard,
190     panes        => \%pane_name,
191 );
192
193 my @panes;
194 for my $pane (keys %pane_name) {
195     my $sel = $m->comp(
196         '/Widgets/SelectionBox:new',
197         Action      => 'Queries.html',
198         Name        => "Searches-$pane",
199         DisplayName => $pane_name{$pane},
200         Available   => \@items,
201         Selected    => $selected{$pane},
202         AutoSave    => 1,
203         OnSubmit    => sub {
204             my $self = shift;
205
206             $m->callback(
207                 CallbackName => 'Submit',
208                 Dashboard    => $Dashboard,
209                 Selected     => $self->{Current},
210                 pane         => $pane,
211             );
212
213             my @portlets;
214             for (@{ $self->{Current} }) {
215                 my $item = $_;
216                 my $desc = $desc_of{$item};
217                 my $portlet_type = $1 if $item =~ s/^(\w+)-//;
218
219                 if ($portlet_type eq 'search' || $portlet_type eq 'dashboard') {
220                     my ($id, $privacy) = split '-', $item, 2;
221                     push @portlets, {
222                         portlet_type => $portlet_type,
223                         privacy      => $privacy,
224                         id           => $id,
225                         description  => $desc,
226                         pane         => $pane,
227                     };
228                 }
229                 elsif ($portlet_type eq 'component') {
230                     # Absolute paths stay absolute, relative paths go into
231                     # /Elements. This way, extensions that add portlets work.
232                     my $path = $item;
233                     $path = "/Elements/$path" if substr($path, 0, 1) ne '/';
234
235                     push @portlets, {
236                         portlet_type => $portlet_type,
237                         component    => $item,
238                         path         => $path,
239                         description  => $item,
240                         pane         => $pane,
241                     };
242                 }
243             }
244
245             # we want to keep all the other panes the same
246             my $panes = $Dashboard->Panes;
247             $panes->{$pane} = \@portlets;
248
249             # remove "deleted" warnings about this pane
250             @deleted = grep { $_->{pane} ne $pane } @deleted;
251
252             $m->callback(
253                 CallbackName => 'BeforeUpdate',
254                 Dashboard    => $Dashboard,
255                 panes        => $panes,
256             );
257
258             my ($ok, $msg) = $Dashboard->Update(Panes => $panes);
259
260             if ($ok) {
261                 push @results, loc("Dashboard updated");
262             }
263             else {
264                 push @results, loc("Dashboard could not be updated: [_1]", $msg);
265             }
266         },
267     );
268
269     push @panes, $sel;
270 }
271
272 $m->comp('/Widgets/SelectionBox:process', %ARGS, self => $_, nojs => 1)
273     for @panes;
274
275 my @hidden_searches = $Dashboard->PossibleHiddenSearches;
276 </%INIT>
277 <%ARGS>
278 $id => '' unless defined $id
279 </%ARGS>
280