1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2011 Best Practical Solutions, LLC
6 # <sales@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
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
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.
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.
30 # CONTRIBUTION SUBMISSION POLICY:
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.)
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.
47 # END BPS TAGGED BLOCK }}}
51 RT::Dashboard - an API for saving and retrieving dashboards
59 Dashboard is an object that can belong to either an RT::User or an
60 RT::Group. It consists of an ID, a name, and a number of
61 saved searches and portlets.
68 package RT::Dashboard;
74 use base qw/RT::SharedSetting/;
77 RT::System::AddRights(
78 SubscribeDashboard => 'Subscribe to dashboards', #loc_pair
80 SeeDashboard => 'View system dashboards', #loc_pair
81 CreateDashboard => 'Create system dashboards', #loc_pair
82 ModifyDashboard => 'Modify system dashboards', #loc_pair
83 DeleteDashboard => 'Delete system dashboards', #loc_pair
85 SeeOwnDashboard => 'View personal dashboards', #loc_pair
86 CreateOwnDashboard => 'Create personal dashboards', #loc_pair
87 ModifyOwnDashboard => 'Modify personal dashboards', #loc_pair
88 DeleteOwnDashboard => 'Delete personal dashboards', #loc_pair
94 An object of this class is called "dashboard"
98 sub ObjectName { "dashboard" }
105 return $object->AddAttribute(
106 'Name' => 'Dashboard',
107 'Description' => $args->{'Name'},
108 'Content' => {Panes => $args->{'Panes'}},
112 sub UpdateAttribute {
116 my ($status, $msg) = (1, undef);
117 if (defined $args->{'Panes'}) {
118 ($status, $msg) = $self->{'Attribute'}->SetSubValues(
119 Panes => $args->{'Panes'},
123 if ($status && $args->{'Name'}) {
124 ($status, $msg) = $self->{'Attribute'}->SetDescription($args->{'Name'})
125 unless $self->Name eq $args->{'Name'};
128 if ($status && $args->{'Privacy'}) {
129 my ($new_obj_type, $new_obj_id) = split /-/, $args->{'Privacy'};
130 my ($obj_type, $obj_id) = split /-/, $self->Privacy;
132 my $attr = $self->{'Attribute'};
133 if ($new_obj_type ne $obj_type) {
134 ($status, $msg) = $attr->SetObjectType($new_obj_type);
136 if ($status && $new_obj_id != $obj_id ) {
137 ($status, $msg) = $attr->SetObjectId($new_obj_id);
139 $self->{'Privacy'} = $args->{'Privacy'} if $status;
142 return ($status, $msg);
147 Returns a hashref of pane name to portlets
153 return unless ref($self->{'Attribute'}) eq 'RT::Attribute';
154 return $self->{'Attribute'}->SubValue('Panes') || {};
159 Returns the list of this dashboard's portlets, each a hashref with key
160 C<portlet_type> being C<search> or C<component>.
166 return map { @$_ } values %{ $self->Panes };
171 Returns a list of loaded sub-dashboards
178 my $search = RT::Dashboard->new($self->CurrentUser);
179 $search->LoadById($_->{id});
181 } grep { $_->{portlet_type} eq 'dashboard' } $self->Portlets;
186 Returns a list of loaded saved searches
193 my $search = RT::SavedSearch->new($self->CurrentUser);
194 $search->Load($_->{privacy}, $_->{id});
196 } grep { $_->{portlet_type} eq 'search' } $self->Portlets;
199 =head2 ShowSearchName Portlet
201 Returns an array for one saved search, suitable for passing to
202 /Elements/ShowSearch.
210 if ($portlet->{privacy} eq 'RT::System') {
211 return Name => $portlet->{description};
214 return SavedSearch => join('-', $portlet->{privacy}, 'SavedSearch', $portlet->{id});
217 =head2 PossibleHiddenSearches
219 This will return a list of saved searches that are potentially not visible by
220 all users for whom the dashboard is visible. You may pass in a privacy to
221 use instead of the dashboard's privacy.
225 sub PossibleHiddenSearches {
227 my $privacy = shift || $self->Privacy;
229 return grep { !$_->IsVisibleTo($privacy) } $self->Searches, $self->Dashboards;
232 # _PrivacyObjects: returns a list of objects that can be used to load
233 # dashboards from. If the Modify parameter is true, then check modify rights.
234 # If the Create parameter is true, then check create rights. Otherwise, check
237 sub _PrivacyObjects {
241 my $CurrentUser = $self->CurrentUser;
244 my $prefix = $args{Modify} ? "Modify"
245 : $args{Create} ? "Create"
248 push @objects, $CurrentUser->UserObj
249 if $self->CurrentUser->HasRight(
250 Right => "${prefix}OwnDashboard",
251 Object => $RT::System,
254 my $groups = RT::Groups->new($CurrentUser);
255 $groups->LimitToUserDefinedGroups;
256 $groups->WithMember( PrincipalId => $CurrentUser->Id,
259 push @objects, grep {
260 $self->CurrentUser->HasRight(
261 Right => "${prefix}GroupDashboard",
264 } @{ $groups->ItemsArrayRef };
266 push @objects, RT::System->new($CurrentUser)
267 if $CurrentUser->HasRight(
268 Right => "${prefix}Dashboard",
269 Object => $RT::System,
277 sub _CurrentUserCan {
279 my $privacy = shift || $self->Privacy;
282 if (!defined($privacy)) {
283 $RT::Logger->debug("No privacy provided to $self->_CurrentUserCan");
287 my $object = $self->_GetObject($privacy);
288 return 0 unless $object;
292 if ($object->isa('RT::User')) { $level = 'Own' }
293 elsif ($object->isa('RT::Group')) { $level = 'Group' }
294 elsif ($object->isa('RT::System')) { $level = '' }
296 $RT::Logger->error("Unknown object $object from privacy $privacy");
300 # users are mildly special-cased, since we actually have to check that
301 # the user is operating on himself
302 if ($object->isa('RT::User')) {
303 return 0 unless $object->Id == $self->CurrentUser->Id;
306 my $right = $args{FullRight}
307 || join('', $args{Right}, $level, 'Dashboard');
309 # all rights, except group rights, are global
310 $object = $RT::System unless $object->isa('RT::Group');
312 return $self->CurrentUser->HasRight(
318 sub CurrentUserCanSee {
322 $self->_CurrentUserCan($privacy, Right => 'See');
325 sub CurrentUserCanCreate {
329 $self->_CurrentUserCan($privacy, Right => 'Create');
332 sub CurrentUserCanModify {
336 $self->_CurrentUserCan($privacy, Right => 'Modify');
339 sub CurrentUserCanDelete {
343 $self->_CurrentUserCan($privacy, Right => 'Delete');
346 sub CurrentUserCanSubscribe {
350 $self->_CurrentUserCan($privacy, FullRight => 'SubscribeDashboard');
353 RT::Base->_ImportOverlays();