1# BEGIN BPS TAGGED BLOCK {{{ 2# 3# COPYRIGHT: 4# 5# This software is Copyright (c) 1996-2021 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 49=head1 NAME 50 51RT::Interface::Web::MenuBuilder 52 53=cut 54 55use strict; 56use warnings; 57 58package RT::Interface::Web::MenuBuilder; 59 60sub loc { HTML::Mason::Commands::loc( @_ ); } 61 62sub QueryString { 63 my %args = @_; 64 my $u = URI->new(); 65 $u->query_form(map { $_ => $args{$_} } sort keys %args); 66 return $u->query; 67} 68 69sub BuildMainNav { 70 my $request_path = shift; 71 my $top = shift; 72 my $widgets = shift; 73 my $page = shift; 74 75 my %args = ( @_ ); 76 77 my $query_string = $args{QueryString}; 78 my $query_args = $args{QueryArgs}; 79 80 my $current_user = $HTML::Mason::Commands::session{CurrentUser}; 81 82 if ($request_path =~ m{^/Asset/}) { 83 $widgets->child( asset_search => raw_html => $HTML::Mason::Commands::m->scomp('/Asset/Elements/Search') ); 84 $widgets->child( create_asset => raw_html => $HTML::Mason::Commands::m->scomp('/Asset/Elements/CreateAsset') ); 85 } 86 elsif ($request_path =~ m{^/Articles/}) { 87 $widgets->child( article_search => raw_html => $HTML::Mason::Commands::m->scomp('/Articles/Elements/GotoArticle') ); 88 $widgets->child( create_article => raw_html => $HTML::Mason::Commands::m->scomp('/Articles/Elements/CreateArticleButton') ); 89 } else { 90 $widgets->child( simple_search => raw_html => $HTML::Mason::Commands::m->scomp('SimpleSearch', Placeholder => loc('Search Tickets')) ); 91 $widgets->child( create_ticket => raw_html => $HTML::Mason::Commands::m->scomp('CreateTicket') ); 92 } 93 94 my $home = $top->child( home => title => loc('Homepage'), path => '/' ); 95 $home->child( create_ticket => title => loc("Create Ticket"), 96 path => "/Ticket/Create.html" ); 97 98 my $search = $top->child( search => title => loc('Search'), path => '/Search/Simple.html' ); 99 100 my $tickets = $search->child( tickets => title => loc('Tickets'), path => '/Search/Build.html' ); 101 $tickets->child( simple => title => loc('Simple Search'), path => "/Search/Simple.html" ); 102 $tickets->child( new => title => loc('New Search'), path => "/Search/Build.html?NewQuery=1" ); 103 104 my $recents = $tickets->child( recent => title => loc('Recently Viewed')); 105 for my $ticket ( $current_user->RecentlyViewedTickets ) { 106 my $title = $ticket->{subject} || loc( "(No subject)" ); 107 if ( length $title > 50 ) { 108 $title = substr($title, 0, 47); 109 $title =~ s/\s+$//; 110 $title .= "..."; 111 } 112 $title = "#$ticket->{id}: " . $title; 113 $recents->child( "$ticket->{id}" => title => $title, path => "/Ticket/Display.html?id=" . $ticket->{id} ); 114 } 115 116 $search->child( articles => title => loc('Articles'), path => "/Articles/Article/Search.html" ) 117 if $current_user->HasRight( Right => 'ShowArticlesMenu', Object => RT->System ); 118 119 $search->child( users => title => loc('Users'), path => "/User/Search.html" ); 120 121 $search->child( groups => 122 title => loc('Groups'), 123 path => "/Group/Search.html", 124 description => 'Group search' 125 ); 126 127 my $search_assets; 128 if ($HTML::Mason::Commands::session{CurrentUser}->HasRight( Right => 'ShowAssetsMenu', Object => RT->System )) { 129 $search_assets = $search->child( assets => title => loc("Assets"), path => "/Search/Build.html?Class=RT::Assets" ); 130 if (!RT->Config->Get('AssetHideSimpleSearch')) { 131 $search_assets->child("asset_simple", title => loc("Simple Search"), path => "/Asset/Search/"); 132 } 133 $search_assets->child("assetsql", title => loc("New Search"), path => "/Search/Build.html?Class=RT::Assets;NewQuery=1"); 134 } 135 136 137 my $txns = $search->child( transactions => title => loc('Transactions'), path => '/Search/Build.html?Class=RT::Transactions;ObjectType=RT::Ticket' ); 138 my $txns_tickets = $txns->child( tickets => title => loc('Tickets'), path => "/Search/Build.html?Class=RT::Transactions;ObjectType=RT::Ticket" ); 139 $txns_tickets->child( new => title => loc('New Search'), path => "/Search/Build.html?Class=RT::Transactions;ObjectType=RT::Ticket;NewQuery=1" ); 140 141 my $reports = $top->child( reports => 142 title => loc('Reports'), 143 description => loc('Reports and Dashboards'), 144 path => loc('/Reports'), 145 ); 146 147 unless ($HTML::Mason::Commands::session{'dashboards_in_menu'}) { 148 my $dashboards_in_menu = $current_user->UserObj->Preferences( 149 'DashboardsInMenu', 150 {}, 151 ); 152 153 unless ($dashboards_in_menu->{dashboards}) { 154 my ($default_dashboards) = 155 RT::System->new( $current_user ) 156 ->Attributes 157 ->Named('DashboardsInMenu'); 158 if ($default_dashboards) { 159 $dashboards_in_menu = $default_dashboards->Content; 160 } 161 } 162 163 $HTML::Mason::Commands::session{'dashboards_in_menu'} = $dashboards_in_menu->{dashboards} || []; 164 } 165 166 my @dashboards; 167 for my $id ( @{$HTML::Mason::Commands::session{'dashboards_in_menu'}} ) { 168 my $dash = RT::Dashboard->new( $current_user ); 169 my ( $status, $msg ) = $dash->LoadById($id); 170 if ( $status ) { 171 push @dashboards, $dash; 172 } else { 173 $RT::Logger->debug( "Failed to load dashboard $id: $msg, removing from menu" ); 174 $home->RemoveDashboardMenuItem( 175 DashboardId => $id, 176 CurrentUser => $HTML::Mason::Commands::session{CurrentUser}->UserObj, 177 ); 178 @{ $HTML::Mason::Commands::session{'dashboards_in_menu'} } = 179 grep { $_ != $id } @{ $HTML::Mason::Commands::session{'dashboards_in_menu'} }; 180 } 181 } 182 183 if (@dashboards) { 184 for my $dash (@dashboards) { 185 $reports->child( 'dashboard-' . $dash->id, 186 title => $dash->Name, 187 path => '/Dashboards/' . $dash->id . '/' . $dash->Name 188 ); 189 } 190 } 191 192 # Get the list of reports in the Reports menu 193 unless ( $HTML::Mason::Commands::session{'reports_in_menu'} 194 && ref( $HTML::Mason::Commands::session{'reports_in_menu'}) eq 'ARRAY' 195 && @{$HTML::Mason::Commands::session{'reports_in_menu'}} 196 ) { 197 my $reports_in_menu = $current_user->UserObj->Preferences( 198 'ReportsInMenu', 199 {}, 200 ); 201 unless ( $reports_in_menu && ref $reports_in_menu eq 'ARRAY' ) { 202 my ($default_reports) = 203 RT::System->new( RT->SystemUser ) 204 ->Attributes 205 ->Named('ReportsInMenu'); 206 if ($default_reports) { 207 $reports_in_menu = $default_reports->Content; 208 } 209 else { 210 $reports_in_menu = []; 211 } 212 } 213 214 $HTML::Mason::Commands::session{'reports_in_menu'} = $reports_in_menu || []; 215 } 216 217 for my $report ( @{$HTML::Mason::Commands::session{'reports_in_menu'}} ) { 218 $reports->child( $report->{id} => 219 title => $report->{title}, 220 path => $report->{path}, 221 ); 222 } 223 224 $reports->child( edit => title => loc('Update This Menu'), path => '/Prefs/DashboardsInMenu.html' ); 225 $reports->child( more => title => loc('All Dashboards'), path => '/Dashboards/index.html' ); 226 my $dashboard = RT::Dashboard->new( $current_user ); 227 if ( $dashboard->CurrentUserCanCreateAny ) { 228 $reports->child('dashboard_create' => title => loc('New Dashboard'), path => "/Dashboards/Modify.html?Create=1" ); 229 } 230 231 232 if ($current_user->HasRight( Right => 'ShowArticlesMenu', Object => RT->System )) { 233 my $articles = $top->child( articles => title => loc('Articles'), path => "/Articles/index.html"); 234 $articles->child( articles => title => loc('Overview'), path => "/Articles/index.html" ); 235 $articles->child( topics => title => loc('Topics'), path => "/Articles/Topics.html" ); 236 $articles->child( create => title => loc('Create'), path => "/Articles/Article/PreCreate.html" ); 237 $articles->child( search => title => loc('Search'), path => "/Articles/Article/Search.html" ); 238 } 239 240 if ($current_user->HasRight( Right => 'ShowAssetsMenu', Object => RT->System )) { 241 my $assets = $top->child( 242 "assets", 243 title => loc("Assets"), 244 path => RT->Config->Get('AssetHideSimpleSearch') ? "/Search/Build.html?Class=RT::Assets;NewQuery=1" : "/Asset/Search/", 245 ); 246 $assets->child( "create", title => loc("Create"), path => "/Asset/Create.html" ); 247 if (!RT->Config->Get('AssetHideSimpleSearch')) { 248 $assets->child( "simple_search", title => loc("Simple Search"), path => "/Asset/Search/" ); 249 } 250 $assets->child( "search", title => loc("New Search"), path => "/Search/Build.html?Class=RT::Assets;NewQuery=1" ); 251 } 252 253 my $tools = $top->child( tools => title => loc('Tools'), path => '/Tools/index.html' ); 254 255 $tools->child( my_day => 256 title => loc('My Day'), 257 description => loc('Easy updating of your open tickets'), 258 path => '/Tools/MyDay.html', 259 ); 260 261 if ( RT->Config->Get('EnableReminders') ) { 262 $tools->child( my_reminders => 263 title => loc('My Reminders'), 264 description => loc('Easy viewing of your reminders'), 265 path => '/Tools/MyReminders.html', 266 ); 267 } 268 269 if ( $current_user->HasRight( Right => 'ShowApprovalsTab', Object => RT->System ) ) { 270 $tools->child( approval => 271 title => loc('Approval'), 272 description => loc('My Approvals'), 273 path => '/Approvals/', 274 ); 275 } 276 277 if ( $current_user->HasRight( Right => 'ShowConfigTab', Object => RT->System ) ) 278 { 279 _BuildAdminMenu( $request_path, $top, $widgets, $page, %args ); 280 } 281 282 my $username = '<span class="current-user">' 283 . $HTML::Mason::Commands::m->interp->apply_escapes($current_user->Name, 'h') 284 . '</span>'; 285 my $about_me = $top->child( 'preferences' => 286 title => loc('Logged in as [_1]', $username), 287 escape_title => 0, 288 path => '/User/Summary.html?id=' . $current_user->id, 289 sort_order => 99, 290 ); 291 292 $about_me->child( rt_name => title => loc("RT for [_1]", RT->Config->Get('rtname')), path => '/' ); 293 294 if ( $current_user->UserObj 295 && $current_user->HasRight( Right => 'ModifySelf', Object => RT->System )) { 296 my $settings = $about_me->child( settings => title => loc('Settings'), path => '/Prefs/Other.html' ); 297 $settings->child( options => title => loc('Preferences'), path => '/Prefs/Other.html' ); 298 $settings->child( about_me => title => loc('About me'), path => '/Prefs/AboutMe.html' ); 299 if ( $current_user->HasRight( Right => 'ManageAuthTokens', Object => RT->System ) ) { 300 $settings->child( auth_tokens => title => loc('Auth Tokens'), path => '/Prefs/AuthTokens.html' ); 301 } 302 $settings->child( search_options => title => loc('Search options'), path => '/Prefs/SearchOptions.html' ); 303 $settings->child( myrt => title => loc('RT at a glance'), path => '/Prefs/MyRT.html' ); 304 $settings->child( dashboards_in_menu => 305 title => loc('Modify Reports menu'), 306 path => '/Prefs/DashboardsInMenu.html', 307 ); 308 $settings->child( queue_list => title => loc('Queue list'), path => '/Prefs/QueueList.html' ); 309 310 my $search_menu = $settings->child( 'saved-searches' => title => loc('Saved Searches') ); 311 my $searches = [ $HTML::Mason::Commands::m->comp( "/Search/Elements/SearchesForObject", 312 Object => RT::System->new( $current_user )) ]; 313 my $i = 0; 314 315 for my $search (@$searches) { 316 $search_menu->child( "search-" . $i++ => 317 title => $search->[1], 318 path => "/Prefs/Search.html?" 319 . QueryString( name => ref( $search->[2] ) . '-' . $search->[2]->Id ), 320 ); 321 322 } 323 324 if ( $request_path =~ qr{/Prefs/(?:SearchOptions|CustomDateRanges)\.html} ) { 325 $page->child( 326 search_options => title => loc('Search Preferences'), 327 path => "/Prefs/SearchOptions.html" 328 ); 329 $page->child( 330 custom_date_ranges => title => loc('Custom Date Ranges'), 331 path => "/Prefs/CustomDateRanges.html" 332 ) 333 } 334 335 if ( $request_path =~ m{^/Prefs/AuthTokens\.html} ) { 336 $page->child( create_auth_token => title => loc('Create'), 337 raw_html => q[<a class="btn menu-item" href="#create-auth-token" data-toggle="modal" rel="modal:open">].loc("Create")."</a>" 338 ); 339 } 340 } 341 my $logout_url = RT->Config->Get('LogoutURL'); 342 if ( $current_user->Name 343 && ( !RT->Config->Get('WebRemoteUserAuth') 344 || RT->Config->Get('WebFallbackToRTLogin') )) { 345 $about_me->child( logout => title => loc('Logout'), path => $logout_url ); 346 } 347 if ( $request_path =~ m{^/Dashboards/(\d+)?}) { 348 if ( my $id = ( $1 || $HTML::Mason::Commands::DECODED_ARGS->{'id'} ) ) { 349 my $obj = RT::Dashboard->new( $current_user ); 350 $obj->LoadById($id); 351 if ( $obj and $obj->id ) { 352 $page->child( basics => title => loc('Basics'), path => "/Dashboards/Modify.html?id=" . $obj->id); 353 $page->child( content => title => loc('Content'), path => "/Dashboards/Queries.html?id=" . $obj->id); 354 $page->child( subscription => title => loc('Subscription'), path => "/Dashboards/Subscription.html?id=" . $obj->id) 355 if $obj->CurrentUserCanSubscribe; 356 $page->child( show => title => loc('Show'), path => "/Dashboards/" . $obj->id . "/" . $obj->Name) 357 } 358 } 359 } 360 361 362 my $search_results_page_menu; 363 if ( $request_path =~ m{^/Ticket/} ) { 364 if ( ( $HTML::Mason::Commands::DECODED_ARGS->{'id'} || '' ) =~ /^(\d+)$/ ) { 365 my $id = $1; 366 my $obj = RT::Ticket->new( $current_user ); 367 $obj->Load($id); 368 369 if ( $obj and $obj->id ) { 370 my $actions = $page->child( actions => title => loc('Actions'), sort_order => 95 ); 371 372 my %can = %{ $obj->CurrentUser->PrincipalObj->HasRights( Object => $obj ) }; 373 # since CurrentUserCanSetOwner returns ($ok, $msg), the parens ensure that $can{} gets $ok 374 ( $can{'_ModifyOwner'} ) = $obj->CurrentUserCanSetOwner(); 375 my $can = sub { 376 unless ($_[0] eq 'ExecuteCode') { 377 return $can{$_[0]} || $can{'SuperUser'}; 378 } else { 379 return !RT->Config->Get('DisallowExecuteCode') 380 && ( $can{'ExecuteCode'} || $can{'SuperUser'} ); 381 } 382 }; 383 384 $page->child( bookmark => raw_html => $HTML::Mason::Commands::m->scomp( '/Ticket/Elements/Bookmark', id => $id ), sort_order => 98 ); 385 386 if ($can->('ModifyTicket')) { 387 $page->child( timer => raw_html => $HTML::Mason::Commands::m->scomp( '/Ticket/Elements/PopupTimerLink', id => $id ), sort_order => 99 ); 388 } 389 390 $page->child( display => title => loc('Display'), path => "/Ticket/Display.html?id=" . $id ); 391 $page->child( history => title => loc('History'), path => "/Ticket/History.html?id=" . $id ); 392 393 # comment out until we can do it for an individual custom field 394 #if ( $can->('ModifyTicket') || $can->('ModifyCustomField') ) { 395 $page->child( basics => title => loc('Basics'), path => "/Ticket/Modify.html?id=" . $id ); 396 397 #} 398 399 if ( $can->('ModifyTicket') || $can->('_ModifyOwner') || $can->('Watch') || $can->('WatchAsAdminCc') ) { 400 $page->child( people => title => loc('People'), path => "/Ticket/ModifyPeople.html?id=" . $id ); 401 } 402 403 if ( $can->('ModifyTicket') ) { 404 $page->child( dates => title => loc('Dates'), path => "/Ticket/ModifyDates.html?id=" . $id ); 405 $page->child( links => title => loc('Links'), path => "/Ticket/ModifyLinks.html?id=" . $id ); 406 } 407 408 #if ( $can->('ModifyTicket') || $can->('ModifyCustomField') || $can->('_ModifyOwner') ) { 409 $page->child( jumbo => title => loc('Jumbo'), path => "/Ticket/ModifyAll.html?id=" . $id ); 410 #} 411 412 if ( RT->Config->Get('EnableReminders') ) { 413 $page->child( reminders => title => loc('Reminders'), path => "/Ticket/Reminders.html?id=" . $id ); 414 } 415 416 if ( $can->('ModifyTicket') or $can->('ReplyToTicket') ) { 417 $actions->child( reply => title => loc('Reply'), path => "/Ticket/Update.html?Action=Respond;id=" . $id ); 418 } 419 420 if ( $can->('ModifyTicket') or $can->('CommentOnTicket') ) { 421 $actions->child( comment => title => loc('Comment'), path => "/Ticket/Update.html?Action=Comment;id=" . $id ); 422 } 423 424 if ( $can->('ForwardMessage') ) { 425 $actions->child( forward => title => loc('Forward'), path => "/Ticket/Forward.html?id=" . $id ); 426 } 427 428 my $hide_resolve_with_deps = RT->Config->Get('HideResolveActionsWithDependencies') 429 && $obj->HasUnresolvedDependencies; 430 431 my $current = $obj->Status; 432 my $lifecycle = $obj->LifecycleObj; 433 my $i = 1; 434 foreach my $info ( $lifecycle->Actions($current) ) { 435 my $next = $info->{'to'}; 436 next unless $lifecycle->IsTransition( $current => $next ); 437 438 my $check = $lifecycle->CheckRight( $current => $next ); 439 next unless $can->($check); 440 441 next if $hide_resolve_with_deps 442 && $lifecycle->IsInactive($next) 443 && !$lifecycle->IsInactive($current); 444 445 my $action = $info->{'update'} || ''; 446 my $url = '/Ticket/'; 447 $url .= "Update.html?". QueryString( 448 $action 449 ? (Action => $action) 450 : (SubmitTicket => 1, Status => $next), 451 DefaultStatus => $next, 452 id => $id, 453 ); 454 my $key = $info->{'label'} || ucfirst($next); 455 $actions->child( $key => title => loc( $key ), path => $url); 456 } 457 458 my ($can_take, $tmsg) = $obj->CurrentUserCanSetOwner( Type => 'Take' ); 459 my ($can_steal, $smsg) = $obj->CurrentUserCanSetOwner( Type => 'Steal' ); 460 my ($can_untake, $umsg) = $obj->CurrentUserCanSetOwner( Type => 'Untake' ); 461 if ( $can_take ){ 462 $actions->child( take => title => loc('Take'), path => "/Ticket/Display.html?Action=Take;id=" . $id ); 463 } 464 elsif ( $can_steal ){ 465 $actions->child( steal => title => loc('Steal'), path => "/Ticket/Display.html?Action=Steal;id=" . $id ); 466 } 467 elsif ( $can_untake ){ 468 $actions->child( untake => title => loc('Untake'), path => "/Ticket/Display.html?Action=Untake;id=" . $id ); 469 } 470 471 # TODO needs a "Can extract article into a class applied to this queue" check 472 $actions->child( 'extract-article' => 473 title => loc('Extract Article'), 474 path => "/Articles/Article/ExtractIntoClass.html?Ticket=".$obj->id, 475 ) if $current_user->HasRight( Right => 'ShowArticlesMenu', Object => RT->System ); 476 477 $actions->child( 'edit_assets' => 478 title => loc('Edit Assets'), 479 path => "/Asset/Search/Bulk.html?Query=Linked=" . $obj->id, 480 ) if $can->('ModifyTicket') 481 && $HTML::Mason::Commands::session{CurrentUser}->HasRight( Right => 'ShowAssetsMenu', Object => RT->System ); 482 483 if ( RT::Config->Get( 'ShowSearchNavigation', $current_user ) 484 && defined $HTML::Mason::Commands::session{"collection-RT::Tickets"} ) 485 { 486 # we have to update session data if we get new ItemMap 487 my $updatesession; 488 $updatesession = 1 unless ( $HTML::Mason::Commands::session{"collection-RT::Tickets"}->{'item_map'} ); 489 490 my $item_map = $HTML::Mason::Commands::session{"collection-RT::Tickets"}->ItemMap; 491 492 if ($updatesession) { 493 $HTML::Mason::Commands::session{"collection-RT::Tickets"}->PrepForSerialization(); 494 } 495 496 my $search = $search_results_page_menu = $HTML::Mason::Commands::m->notes('search-results-page-menu', RT::Interface::Web::Menu->new()); 497 498 # Don't display prev links if we're on the first ticket 499 if ( $item_map->{$id}->{prev} ) { 500 $search->child( 501 first => title => q{<span class="fas fa-angle-double-left"></span>}, 502 escape_title => 0, 503 class => "nav", 504 path => "/Ticket/Display.html?id=" . $item_map->{first}, 505 attributes => { 506 'data-toggle' => 'tooltip', 507 'data-original-title' => loc('First'), 508 alt => loc('First'), 509 }, 510 ); 511 $search->child( 512 prev => title => q{<span class="fas fa-angle-left"></span>}, 513 escape_title => 0, 514 class => "nav", 515 path => "/Ticket/Display.html?id=" . $item_map->{$id}->{prev}, 516 attributes => { 517 'data-toggle' => 'tooltip', 518 'data-original-title' => loc('Prev'), 519 alt => loc('Prev'), 520 }, 521 ); 522 } 523 # Don't display next links if we're on the last ticket 524 if ( $item_map->{$id}->{next} ) { 525 $search->child( 526 next => title => q{<span class="fas fa-angle-right"></span>}, 527 escape_title => 0, 528 class => "nav", 529 path => "/Ticket/Display.html?id=" . $item_map->{$id}->{next}, 530 attributes => { 531 'data-toggle' => 'tooltip', 532 'data-original-title' => loc('Next'), 533 alt => loc('Next'), 534 }, 535 ); 536 if ( $item_map->{last} ) { 537 $search->child( 538 last => title => q{<span class="fas fa-angle-double-right"></span>}, 539 escape_title => 0, 540 class => "nav", 541 path => "/Ticket/Display.html?id=" . $item_map->{last}, 542 attributes => { 543 'data-toggle' => 'tooltip', 544 'data-original-title' => loc('Last'), 545 alt => loc('Last'), 546 }, 547 ); 548 } 549 } 550 } 551 } 552 } 553 } 554 555 # display "View ticket" link in transactions 556 if ( $request_path =~ m{^/Transaction/Display.html} ) { 557 if ( ( $HTML::Mason::Commands::DECODED_ARGS->{'id'} || '' ) =~ /^(\d+)$/ ) { 558 my $txn_id = $1; 559 my $txn = RT::Transaction->new( $current_user ); 560 $txn->Load( $txn_id ); 561 my $object = $txn->Object; 562 if ( $object->Id ) { 563 my $object_type = $object->RecordType; 564 if ( $object_type eq 'Ticket' ) { 565 $page->child( view_ticket => title => loc("View ticket"), path => "/Ticket/Display.html?id=" . $object->Id ); 566 } 567 } 568 } 569 } 570 571 # Scope here so we can share in the Privileged callback 572 my $args = ''; 573 my $has_query = ''; 574 if ( 575 ( 576 $request_path =~ m{^/(?:Ticket|Transaction|Search)/} 577 && $request_path !~ m{^/Search/Simple\.html} 578 ) 579 || ( $request_path =~ m{^/Search/Simple\.html} 580 && $HTML::Mason::Commands::DECODED_ARGS->{'q'} ) 581 582 # TODO: Asset simple search and SQL search don't share query, we 583 # can't simply link to SQL search page on asset pages without 584 # identifying if it's from simple search or SQL search. For now, 585 # show "Current Search" only if asset simple search is disabled. 586 587 || ( $search_assets && $request_path =~ m{^/Asset/(?!Search/)} && RT->Config->Get('AssetHideSimpleSearch') ) 588 ) 589 { 590 my $class = $HTML::Mason::Commands::DECODED_ARGS->{Class} 591 || ( $request_path =~ m{^/(Transaction|Ticket|Asset)/} ? "RT::$1s" : 'RT::Tickets' ); 592 593 my $search; 594 if ( $class eq 'RT::Tickets' ) { 595 $search = $top->child('search')->child('tickets'); 596 } 597 elsif ( $class eq 'RT::Assets' ) { 598 $search = $search_assets; 599 } 600 else { 601 $search = $txns_tickets; 602 } 603 604 my $hash_name = join '-', 'CurrentSearchHash', $class, 605 $HTML::Mason::Commands::DECODED_ARGS->{ObjectType} || ( $class eq 'RT::Transactions' ? 'RT::Ticket' : () ); 606 my $current_search = $HTML::Mason::Commands::session{$hash_name} || {}; 607 my $search_id = $HTML::Mason::Commands::DECODED_ARGS->{'SavedSearchLoad'} || $HTML::Mason::Commands::DECODED_ARGS->{'SavedSearchId'} || $current_search->{'SearchId'} || ''; 608 my $chart_id = $HTML::Mason::Commands::DECODED_ARGS->{'SavedChartSearchId'} || $current_search->{SavedChartSearchId}; 609 610 $has_query = 1 if ( $HTML::Mason::Commands::DECODED_ARGS->{'Query'} or $current_search->{'Query'} ); 611 612 my %query_args; 613 my %fallback_query_args = ( 614 SavedSearchId => ( $search_id eq 'new' ) ? undef : $search_id, 615 SavedChartSearchId => $chart_id, 616 ( 617 map { 618 my $p = $_; 619 $p => $HTML::Mason::Commands::DECODED_ARGS->{$p} || $current_search->{$p} 620 } qw(Query Format OrderBy Order Page Class ObjectType ResultPage ExtraQueryParams), 621 ), 622 ); 623 624 if ( defined ( my $rows = $HTML::Mason::Commands::DECODED_ARGS->{'RowsPerPage'} // $current_search->{'RowsPerPage'} ) ) { 625 $fallback_query_args{RowsPerPage} = $rows; 626 } 627 628 if ( my $extra_params = $fallback_query_args{ExtraQueryParams} ) { 629 for my $param ( ref $extra_params eq 'ARRAY' ? @$extra_params : $extra_params ) { 630 $fallback_query_args{$param} 631 = $HTML::Mason::Commands::DECODED_ARGS->{$param} || $current_search->{$param}; 632 } 633 } 634 635 $fallback_query_args{Class} ||= $class; 636 $fallback_query_args{ObjectType} ||= 'RT::Ticket' if $class eq 'RT::Transactions'; 637 638 if ($query_string) { 639 $args = '?' . $query_string; 640 } 641 else { 642 my %final_query_args = (); 643 # key => callback to avoid unnecessary work 644 645 if ( my $extra_params = $query_args->{ExtraQueryParams} ) { 646 $final_query_args{ExtraQueryParams} = $extra_params; 647 for my $param ( ref $extra_params eq 'ARRAY' ? @$extra_params : $extra_params ) { 648 $final_query_args{$param} = $query_args->{$param}; 649 } 650 } 651 652 for my $param (keys %fallback_query_args) { 653 $final_query_args{$param} = defined($query_args->{$param}) 654 ? $query_args->{$param} 655 : $fallback_query_args{$param}; 656 } 657 658 for my $field (qw(Order OrderBy)) { 659 if ( ref( $final_query_args{$field} ) eq 'ARRAY' ) { 660 $final_query_args{$field} = join( "|", @{ $final_query_args{$field} } ); 661 } elsif (not defined $final_query_args{$field}) { 662 delete $final_query_args{$field}; 663 } 664 else { 665 $final_query_args{$field} ||= ''; 666 } 667 } 668 669 $args = '?' . QueryString(%final_query_args); 670 } 671 672 my $current_search_menu; 673 if ( $class eq 'RT::Tickets' && $request_path =~ m{^/Ticket} 674 || $class eq 'RT::Transactions' && $request_path =~ m{^/Transaction} 675 || $class eq 'RT::Assets' && $request_path =~ m{^/Asset/(?!Search/)} ) 676 { 677 $current_search_menu = $search->child( current_search => title => loc('Current Search') ); 678 $current_search_menu->path("/Search/Results.html$args") if $has_query; 679 680 if ( $search_results_page_menu && $has_query ) { 681 $search_results_page_menu->child( 682 current_search => title => q{<span class="fas fa-list"></span>}, 683 escape_title => 0, 684 sort_order => -1, 685 path => "/Search/Results.html$args", 686 attributes => { 687 'data-toggle' => 'tooltip', 688 'data-original-title' => loc('Return to Search Results'), 689 alt => loc('Return to Search Results'), 690 }, 691 ); 692 } 693 } 694 else { 695 $current_search_menu = $page; 696 } 697 698 $current_search_menu->child( edit_search => 699 title => loc('Edit Search'), path => "/Search/Build.html" . ( ($has_query) ? $args : '' ) ); 700 if ( $current_user->HasRight( Right => 'ShowSearchAdvanced', Object => RT->System ) ) { 701 $current_search_menu->child( advanced => title => loc('Advanced'), path => "/Search/Edit.html$args" ); 702 } 703 if ($has_query) { 704 my $result_page = $HTML::Mason::Commands::DECODED_ARGS->{ResultPage}; 705 if ( $result_page ) { 706 if ( my $web_path = RT->Config->Get('WebPath') ) { 707 $result_page =~ s!^$web_path!!; 708 } 709 } 710 else { 711 $result_page = '/Search/Results.html'; 712 } 713 714 $current_search_menu->child( results => title => loc('Show Results'), path => "$result_page$args" ); 715 } 716 717 if ( $has_query ) { 718 if ( $class eq 'RT::Tickets' ) { 719 if ( $current_user->HasRight( Right => 'ShowSearchBulkUpdate', Object => RT->System ) ) { 720 $current_search_menu->child( bulk => title => loc('Bulk Update'), path => "/Search/Bulk.html$args" ); 721 } 722 $current_search_menu->child( chart => title => loc('Chart'), path => "/Search/Chart.html$args" ); 723 } 724 elsif ( $class eq 'RT::Assets' ) { 725 $current_search_menu->child( bulk => title => loc('Bulk Update'), path => "/Asset/Search/Bulk.html$args" ); 726 } 727 728 my $more = $current_search_menu->child( more => title => loc('Feeds') ); 729 730 $more->child( spreadsheet => title => loc('Spreadsheet'), path => "/Search/Results.tsv$args" ); 731 732 if ( $class eq 'RT::Tickets' ) { 733 my %rss_data 734 = map { $_ => $query_args->{$_} || $fallback_query_args{$_} || '' } qw(Query Order OrderBy); 735 my $RSSQueryString = "?" 736 . QueryString( 737 Query => $rss_data{Query}, 738 Order => $rss_data{Order}, 739 OrderBy => $rss_data{OrderBy} 740 ); 741 my $RSSPath = join '/', map $HTML::Mason::Commands::m->interp->apply_escapes( $_, 'u' ), 742 $current_user->UserObj->Name, 743 $current_user->UserObj->GenerateAuthString( 744 $rss_data{Query} . $rss_data{Order} . $rss_data{OrderBy} ); 745 746 $more->child( rss => title => loc('RSS'), path => "/NoAuth/rss/$RSSPath/$RSSQueryString" ); 747 my $ical_path = join '/', map $HTML::Mason::Commands::m->interp->apply_escapes( $_, 'u' ), 748 $current_user->UserObj->Name, 749 $current_user->UserObj->GenerateAuthString( $rss_data{Query} ), 750 $rss_data{Query}; 751 $more->child( ical => title => loc('iCal'), path => '/NoAuth/iCal/' . $ical_path ); 752 753 #XXX TODO better abstraction of SuperUser right check 754 if ( $current_user->HasRight( Right => 'SuperUser', Object => RT->System ) ) { 755 my $shred_args = QueryString( 756 Search => 1, 757 Plugin => 'Tickets', 758 'Tickets:query' => $rss_data{'Query'}, 759 'Tickets:limit' => $query_args->{'Rows'}, 760 ); 761 762 $more->child( 763 shredder => title => loc('Shredder'), 764 path => '/Admin/Tools/Shredder/?' . $shred_args 765 ); 766 } 767 } 768 } 769 } 770 771 if ( $request_path =~ m{^/Article/} ) { 772 if ( $HTML::Mason::Commands::DECODED_ARGS->{'id'} && $HTML::Mason::Commands::DECODED_ARGS->{'id'} =~ /^\d+$/ ) { 773 my $id = $HTML::Mason::Commands::DECODED_ARGS->{'id'}; 774 $page->child( display => title => loc('Display'), path => "/Articles/Article/Display.html?id=".$id ); 775 $page->child( history => title => loc('History'), path => "/Articles/Article/History.html?id=".$id ); 776 $page->child( modify => title => loc('Modify'), path => "/Articles/Article/Edit.html?id=".$id ); 777 } 778 } 779 780 if ( $request_path =~ m{^/Articles/} ) { 781 $page->child( search => title => loc("Search"), path => "/Articles/Article/Search.html" ); 782 if ( $request_path =~ m{^/Articles/Article/} and ( $HTML::Mason::Commands::DECODED_ARGS->{'id'} || '' ) =~ /^(\d+)$/ ) { 783 my $id = $1; 784 my $obj = RT::Article->new( $current_user ); 785 $obj->Load($id); 786 787 if ( $obj and $obj->id ) { 788 $page->child( display => title => loc("Display"), path => "/Articles/Article/Display.html?id=" . $id ); 789 $page->child( history => title => loc('History'), path => '/Articles/Article/History.html?id=' . $id ); 790 791 if ( $obj->CurrentUserHasRight('ModifyArticle') ) { 792 $page->child(modify => title => loc('Modify'), path => '/Articles/Article/Edit.html?id=' . $id ); 793 } 794 } 795 } 796 797 } 798 799 if ($request_path =~ m{^/Asset/} and $HTML::Mason::Commands::DECODED_ARGS->{id} and $HTML::Mason::Commands::DECODED_ARGS->{id} !~ /\D/) { 800 _BuildAssetMenu( $request_path, $top, $widgets, $page, %args ); 801 } elsif ( $request_path =~ m{^/Asset/Search/(?:index\.html)?$} 802 || ( $request_path =~ m{^/Asset/Search/Bulk\.html$} && $HTML::Mason::Commands::DECODED_ARGS->{Catalog} ) ) { 803 my %search = map @{$_}, 804 grep defined $_->[1] && length $_->[1], 805 map {ref $HTML::Mason::Commands::DECODED_ARGS->{$_} ? [$_, $HTML::Mason::Commands::DECODED_ARGS->{$_}[0]] : [$_, $HTML::Mason::Commands::DECODED_ARGS->{$_}] } 806 grep /^(?:q|SearchAssets|!?(Name|Description|Catalog|Status|Role\..+|CF\..+)|Order(?:By)?|Page)$/, 807 keys %$HTML::Mason::Commands::DECODED_ARGS; 808 809 if ( $request_path =~ /Bulk/) { 810 $page->child('search', 811 title => loc('Show Results'), 812 path => '/Asset/Search/?' . (keys %search ? QueryString(%search) : ''), 813 ); 814 } else { 815 $page->child('bulk', 816 title => loc('Bulk Update'), 817 path => '/Asset/Search/Bulk.html?' . (keys %search ? QueryString(%search) : ''), 818 ); 819 } 820 821 $page->child('csv', 822 title => loc('Download Spreadsheet'), 823 path => '/Search/Results.tsv?' . QueryString(%search, Class => 'RT::Assets'), 824 ); 825 } elsif ($request_path =~ m{^/Asset/Search/}) { 826 my %search = map @{$_}, 827 grep defined $_->[1] && length $_->[1], 828 map {ref $HTML::Mason::Commands::DECODED_ARGS->{$_} ? [$_, $HTML::Mason::Commands::DECODED_ARGS->{$_}[0]] : [$_, $HTML::Mason::Commands::DECODED_ARGS->{$_}] } 829 grep /^(?:q|SearchAssets|!?(Name|Description|Catalog|Status|Role\..+|CF\..+)|Order(?:By)?|Page)$/, 830 keys %$HTML::Mason::Commands::DECODED_ARGS; 831 832 my $current_search = $HTML::Mason::Commands::session{"CurrentSearchHash-RT::Assets"} || {}; 833 my $search_id = $HTML::Mason::Commands::DECODED_ARGS->{'SavedSearchLoad'} || $HTML::Mason::Commands::DECODED_ARGS->{'SavedSearchId'} || $current_search->{'SearchId'} || ''; 834 my $args = ''; 835 my $has_query; 836 $has_query = 1 if ( $HTML::Mason::Commands::DECODED_ARGS->{'Query'} or $current_search->{'Query'} ); 837 838 my %query_args; 839 my %fallback_query_args = ( 840 Class => 'RT::Assets', 841 SavedSearchId => ( $search_id eq 'new' ) ? undef : $search_id, 842 ( 843 map { 844 my $p = $_; 845 $p => $HTML::Mason::Commands::DECODED_ARGS->{$p} || $current_search->{$p} 846 } qw(Query Format OrderBy Order Page) 847 ), 848 ); 849 850 if ( defined ( my $rows = $HTML::Mason::Commands::DECODED_ARGS->{'RowsPerPage'} // $current_search->{'RowsPerPage'} ) ) { 851 $fallback_query_args{RowsPerPage} = $rows; 852 } 853 854 if ($query_string) { 855 $args = '?' . $query_string; 856 } 857 else { 858 my %final_query_args = (); 859 # key => callback to avoid unnecessary work 860 861 for my $param (keys %fallback_query_args) { 862 $final_query_args{$param} = defined($query_args->{$param}) 863 ? $query_args->{$param} 864 : $fallback_query_args{$param}; 865 } 866 867 for my $field (qw(Order OrderBy)) { 868 if ( ref( $final_query_args{$field} ) eq 'ARRAY' ) { 869 $final_query_args{$field} = join( "|", @{ $final_query_args{$field} } ); 870 } elsif (not defined $final_query_args{$field}) { 871 delete $final_query_args{$field}; 872 } 873 else { 874 $final_query_args{$field} ||= ''; 875 } 876 } 877 878 $args = '?' . QueryString(%final_query_args); 879 } 880 881 $page->child('edit_search', 882 title => loc('Edit Search'), 883 path => '/Search/Build.html' . $args, 884 ); 885 $page->child( advanced => title => loc('Advanced'), path => '/Search/Edit.html' . $args ); 886 if ($has_query) { 887 $page->child( results => title => loc('Show Results'), path => '/Search/Results.html' . $args ); 888 $page->child('bulk', 889 title => loc('Bulk Update'), 890 path => '/Asset/Search/Bulk.html' . $args, 891 ); 892 my $more = $page->child( more => title => loc('Feeds') ); 893 $more->child( spreadsheet => title => loc('Spreadsheet'), path => "/Search/Results.tsv$args" ); 894 } 895 } elsif ($request_path =~ m{^/Admin/Global/CustomFields/Catalog-Assets\.html$}) { 896 $page->child("create", title => loc("Create New"), path => "/Admin/CustomFields/Modify.html?Create=1;LookupType=" . RT::Asset->CustomFieldLookupType); 897 } elsif ($request_path =~ m{^/Admin/CustomFields(/|/index\.html)?$} 898 and $HTML::Mason::Commands::DECODED_ARGS->{'Type'} and $HTML::Mason::Commands::DECODED_ARGS->{'Type'} eq RT::Asset->CustomFieldLookupType) { 899 $page->child("create")->path( $page->child("create")->path . ";LookupType=" . RT::Asset->CustomFieldLookupType ); 900 } elsif ($request_path =~ m{^/Admin/Assets/Catalogs/}) { 901 my $actions = $request_path =~ m{/((index|Create)\.html)?$} 902 ? $page 903 : $page->child("catalogs", title => loc("Catalogs"), path => "/Admin/Assets/Catalogs/"); 904 905 $actions->child("select", title => loc("Select"), path => "/Admin/Assets/Catalogs/"); 906 $actions->child("create", title => loc("Create"), path => "/Admin/Assets/Catalogs/Create.html"); 907 908 my $catalog = RT::Catalog->new( $current_user ); 909 $catalog->Load($HTML::Mason::Commands::DECODED_ARGS->{id}) if $HTML::Mason::Commands::DECODED_ARGS->{id}; 910 911 if ($catalog->id and $catalog->CurrentUserCanSee) { 912 my $query = "id=" . $catalog->id; 913 $page->child("modify", title => loc("Basics"), path => "/Admin/Assets/Catalogs/Modify.html?$query"); 914 $page->child("people", title => loc("Roles"), path => "/Admin/Assets/Catalogs/Roles.html?$query"); 915 916 $page->child("cfs", title => loc("Asset Custom Fields"), path => "/Admin/Assets/Catalogs/CustomFields.html?$query"); 917 918 $page->child("group-rights", title => loc("Group Rights"), path => "/Admin/Assets/Catalogs/GroupRights.html?$query"); 919 $page->child("user-rights", title => loc("User Rights"), path => "/Admin/Assets/Catalogs/UserRights.html?$query"); 920 921 $page->child("default-values", title => loc('Default Values'), path => "/Admin/Assets/Catalogs/DefaultValues.html?$query"); 922 } 923 } 924 925 if ( $request_path =~ m{^/User/(Summary|History)\.html} ) { 926 if ($page->child('summary')) { 927 # Already set up from having AdminUser and ShowConfigTab; 928 # but rename "Basics" to "Edit" in this context 929 $page->child( 'basics' )->title( loc('Edit') ); 930 } elsif ( $current_user->HasRight( Object => $RT::System, Right => 'ShowUserHistory' ) ) { 931 $page->child( display => title => loc('Summary'), path => '/User/Summary.html?id=' . $HTML::Mason::Commands::DECODED_ARGS->{'id'} ); 932 $page->child( history => title => loc('History'), path => '/User/History.html?id=' . $HTML::Mason::Commands::DECODED_ARGS->{'id'} ); 933 } 934 } 935 936 # Top menu already has the create link, adding it to page menu is just 937 # for convenience. As user admin page already has quite a few items in 938 # page menu and it's unlikely that admins want to create new dashboard 939 # when editing a user's preference, here we don't touch admin user page. 940 if ( $request_path =~ m{^/(Prefs|Admin/Global)/MyRT\.html} ) { 941 if ( RT::Dashboard->new($current_user)->CurrentUserCanCreateAny ) { 942 $page->child( 943 'dashboard_create' => title => loc('New Dashboard'), 944 path => "/Dashboards/Modify.html?Create=1" 945 ); 946 } 947 } 948 949 950 if ( $request_path =~ /^\/(?:index.html|$)/ ) { 951 my $alt = loc('Edit'); 952 $page->child( edit => raw_html => q[<a id="page-edit" class="menu-item" href="] . RT->Config->Get('WebPath') . qq[/Prefs/MyRT.html"><span class="fas fa-cog" alt="$alt" data-toggle="tooltip" data-placement="top" data-original-title="$alt"></span></a>] ); 953 } 954 955 if ( $request_path =~ m{^/Admin/Tools/(Configuration|EditConfig|ConfigHistory)} ) { 956 $page->child( display => title => loc('View'), path => "/Admin/Tools/Configuration.html" ); 957 $page->child( modify => title => loc('Edit'), path => "/Admin/Tools/EditConfig.html" ) if RT->Config->Get('ShowEditSystemConfig'); 958 $page->child( history => title => loc('History'), path => "/Admin/Tools/ConfigHistory.html" ); 959 } 960 961 # due to historical reasons of always having been in /Elements/Tabs 962 $HTML::Mason::Commands::m->callback( CallbackName => 'Privileged', Path => $request_path, Search_Args => $args, Has_Query => $has_query, ARGSRef => \%args, CallbackPage => '/Elements/Tabs' ); 963} 964 965sub _BuildAssetMenu { 966 my $request_path = shift; 967 my $top = shift; 968 my $widgets = shift; 969 my $page = shift; 970 971 my %args = ( @_ ); 972 973 my $current_user = $HTML::Mason::Commands::session{CurrentUser}; 974 975 my $id = $HTML::Mason::Commands::DECODED_ARGS->{id}; 976 my $asset = RT::Asset->new( $current_user ); 977 $asset->Load($id); 978 979 if ($asset->id) { 980 $page->child("display", title => HTML::Mason::Commands::loc("Display"), path => "/Asset/Display.html?id=$id"); 981 $page->child("history", title => HTML::Mason::Commands::loc("History"), path => "/Asset/History.html?id=$id"); 982 $page->child("basics", title => HTML::Mason::Commands::loc("Basics"), path => "/Asset/Modify.html?id=$id"); 983 $page->child("links", title => HTML::Mason::Commands::loc("Links"), path => "/Asset/ModifyLinks.html?id=$id"); 984 $page->child("people", title => HTML::Mason::Commands::loc("People"), path => "/Asset/ModifyPeople.html?id=$id"); 985 $page->child("dates", title => HTML::Mason::Commands::loc("Dates"), path => "/Asset/ModifyDates.html?id=$id"); 986 987 for my $grouping (RT::CustomField->CustomGroupings($asset)) { 988 my $cfs = $asset->CustomFields; 989 $cfs->LimitToGrouping( $asset => $grouping ); 990 next unless $cfs->Count; 991 $page->child( 992 "cf-grouping-$grouping", 993 title => HTML::Mason::Commands::loc($grouping), 994 path => "/Asset/ModifyCFs.html?id=$id;Grouping=" . $HTML::Mason::Commands::m->interp->apply_escapes($grouping, 'u'), 995 ); 996 } 997 998 _BuildAssetMenuActionSubmenu( $request_path, $top, $widgets, $page, %args, Asset => $asset ); 999 } 1000} 1001 1002sub _BuildAssetMenuActionSubmenu { 1003 my $request_path = shift; 1004 my $top = shift; 1005 my $widgets = shift; 1006 my $page = shift; 1007 1008 my %args = ( 1009 Asset => undef, 1010 @_ 1011 ); 1012 1013 my $asset = $args{Asset}; 1014 my $id = $asset->id; 1015 1016 my $actions = $page->child("actions", title => HTML::Mason::Commands::loc("Actions")); 1017 $actions->child("create-linked-ticket", title => HTML::Mason::Commands::loc("Create linked ticket"), path => "/Asset/CreateLinkedTicket.html?Asset=$id"); 1018 1019 my $status = $asset->Status; 1020 my $lifecycle = $asset->LifecycleObj; 1021 for my $action ( $lifecycle->Actions($status) ) { 1022 my $next = $action->{'to'}; 1023 next unless $lifecycle->IsTransition( $status => $next ); 1024 1025 my $check = $lifecycle->CheckRight( $status => $next ); 1026 next unless $asset->CurrentUserHasRight($check); 1027 1028 my $label = $action->{'label'} || ucfirst($next); 1029 $actions->child( 1030 $label, 1031 title => HTML::Mason::Commands::loc($label), 1032 path => "/Asset/Modify.html?id=$id;Update=1;DisplayAfter=1;Status=" 1033 . $HTML::Mason::Commands::m->interp->apply_escapes($next, 'u'), 1034 1035 class => "asset-lifecycle-action", 1036 attributes => { 1037 'data-current-status' => $status, 1038 'data-next-status' => $next, 1039 }, 1040 ); 1041 } 1042} 1043 1044sub _BuildAdminMenu { 1045 my $request_path = shift; 1046 my $top = shift; 1047 my $widgets = shift; 1048 my $page = shift; 1049 1050 my %args = ( @_ ); 1051 1052 my $current_user = $HTML::Mason::Commands::session{CurrentUser}; 1053 1054 my $admin = $top->child( admin => title => loc('Admin'), path => '/Admin/' ); 1055 if ( $current_user->HasRight( Object => RT->System, Right => 'AdminUsers' ) ) { 1056 my $users = $admin->child( users => 1057 title => loc('Users'), 1058 description => loc('Manage users and passwords'), 1059 path => '/Admin/Users/', 1060 ); 1061 $users->child( select => title => loc('Select'), path => "/Admin/Users/" ); 1062 $users->child( create => title => loc('Create'), path => "/Admin/Users/Modify.html?Create=1" ); 1063 } 1064 my $groups = $admin->child( groups => 1065 title => loc('Groups'), 1066 description => loc('Manage groups and group membership'), 1067 path => '/Admin/Groups/', 1068 ); 1069 $groups->child( select => title => loc('Select'), path => "/Admin/Groups/" ); 1070 $groups->child( create => title => loc('Create'), path => "/Admin/Groups/Modify.html?Create=1" ); 1071 1072 my $queues = $admin->child( queues => 1073 title => loc('Queues'), 1074 description => loc('Manage queues and queue-specific properties'), 1075 path => '/Admin/Queues/', 1076 ); 1077 $queues->child( select => title => loc('Select'), path => "/Admin/Queues/" ); 1078 $queues->child( create => title => loc('Create'), path => "/Admin/Queues/Modify.html?Create=1" ); 1079 1080 if ( $current_user->HasRight( Object => RT->System, Right => 'AdminCustomField' ) ) { 1081 my $cfs = $admin->child( 'custom-fields' => 1082 title => loc('Custom Fields'), 1083 description => loc('Manage custom fields and custom field values'), 1084 path => '/Admin/CustomFields/', 1085 ); 1086 $cfs->child( select => title => loc('Select'), path => "/Admin/CustomFields/" ); 1087 $cfs->child( create => title => loc('Create'), path => "/Admin/CustomFields/Modify.html?Create=1" ); 1088 } 1089 1090 if ( $current_user->HasRight( Object => RT->System, Right => 'AdminCustomRoles' ) ) { 1091 my $roles = $admin->child( 'custom-roles' => 1092 title => loc('Custom Roles'), 1093 description => loc('Manage custom roles'), 1094 path => '/Admin/CustomRoles/', 1095 ); 1096 $roles->child( select => title => loc('Select'), path => "/Admin/CustomRoles/" ); 1097 $roles->child( create => title => loc('Create'), path => "/Admin/CustomRoles/Modify.html?Create=1" ); 1098 } 1099 1100 if ( $current_user->HasRight( Object => RT->System, Right => 'ModifyScrips' ) ) { 1101 my $scrips = $admin->child( 'scrips' => 1102 title => loc('Scrips'), 1103 description => loc('Manage scrips'), 1104 path => '/Admin/Scrips/', 1105 ); 1106 $scrips->child( select => title => loc('Select'), path => "/Admin/Scrips/" ); 1107 $scrips->child( create => title => loc('Create'), path => "/Admin/Scrips/Create.html" ); 1108 } 1109 1110 if ( RT->Config->Get('ShowEditLifecycleConfig') 1111 && $current_user->HasRight( Object => RT->System, Right => 'SuperUser' ) ) 1112 { 1113 my $lifecycles = $admin->child( 1114 lifecycles => title => loc('Lifecycles'), 1115 path => '/Admin/Lifecycles/', 1116 ); 1117 1118 $lifecycles->child( select => title => loc('Select'), path => '/Admin/Lifecycles/' ); 1119 $lifecycles->child( create => title => loc('Create'), path => '/Admin/Lifecycles/Create.html' ); 1120 } 1121 1122 my $admin_global = $admin->child( global => 1123 title => loc('Global'), 1124 description => loc('Manage properties and configuration which apply to all queues'), 1125 path => '/Admin/Global/', 1126 ); 1127 1128 my $scrips = $admin_global->child( scrips => 1129 title => loc('Scrips'), 1130 description => loc('Modify scrips which apply to all queues'), 1131 path => '/Admin/Global/Scrips.html', 1132 ); 1133 $scrips->child( select => title => loc('Select'), path => "/Admin/Global/Scrips.html" ); 1134 $scrips->child( create => title => loc('Create'), path => "/Admin/Scrips/Create.html?Global=1" ); 1135 1136 my $conditions = $admin_global->child( conditions => 1137 title => loc('Conditions'), 1138 description => loc('Edit system conditions'), 1139 path => '/Admin/Global/Conditions.html', 1140 ); 1141 $conditions->child( select => title => loc('Select'), path => "/Admin/Global/Conditions.html" ); 1142 $conditions->child( create => title => loc('Create'), path => "/Admin/Conditions/Create.html" ); 1143 1144 my $actions = $admin_global->child( actions => 1145 title => loc('Actions'), 1146 description => loc('Edit system actions'), 1147 path => '/Admin/Global/Actions.html', 1148 ); 1149 $actions->child( select => title => loc('Select'), path => "/Admin/Global/Actions.html" ); 1150 $actions->child( create => title => loc('Create'), path => "/Admin/Actions/Create.html" ); 1151 1152 my $templates = $admin_global->child( templates => 1153 title => loc('Templates'), 1154 description => loc('Edit system templates'), 1155 path => '/Admin/Global/Templates.html', 1156 ); 1157 $templates->child( select => title => loc('Select'), path => "/Admin/Global/Templates.html" ); 1158 $templates->child( create => title => loc('Create'), path => "/Admin/Global/Template.html?Create=1" ); 1159 1160 my $cfadmin = $admin_global->child( 'custom-fields' => 1161 title => loc('Custom Fields'), 1162 description => loc('Modify global custom fields'), 1163 path => '/Admin/Global/CustomFields/index.html', 1164 ); 1165 $cfadmin->child( users => 1166 title => loc('Users'), 1167 description => loc('Select custom fields for all users'), 1168 path => '/Admin/Global/CustomFields/Users.html', 1169 ); 1170 $cfadmin->child( groups => 1171 title => loc('Groups'), 1172 description => loc('Select custom fields for all user groups'), 1173 path => '/Admin/Global/CustomFields/Groups.html', 1174 ); 1175 $cfadmin->child( queues => 1176 title => loc('Queues'), 1177 description => loc('Select custom fields for all queues'), 1178 path => '/Admin/Global/CustomFields/Queues.html', 1179 ); 1180 $cfadmin->child( tickets => 1181 title => loc('Tickets'), 1182 description => loc('Select custom fields for tickets in all queues'), 1183 path => '/Admin/Global/CustomFields/Queue-Tickets.html', 1184 ); 1185 $cfadmin->child( transactions => 1186 title => loc('Ticket Transactions'), 1187 description => loc('Select custom fields for transactions on tickets in all queues'), 1188 path => '/Admin/Global/CustomFields/Queue-Transactions.html', 1189 ); 1190 $cfadmin->child( 'custom-fields' => 1191 title => loc('Articles'), 1192 description => loc('Select Custom Fields for Articles in all Classes'), 1193 path => '/Admin/Global/CustomFields/Class-Article.html', 1194 ); 1195 $cfadmin->child( 'assets' => 1196 title => loc('Assets'), 1197 description => loc('Select Custom Fields for Assets in all Catalogs'), 1198 path => '/Admin/Global/CustomFields/Catalog-Assets.html', 1199 ); 1200 1201 my $article_admin = $admin->child( articles => title => loc('Articles'), path => "/Admin/Articles/index.html" ); 1202 my $class_admin = $article_admin->child(classes => title => loc('Classes'), path => '/Admin/Articles/Classes/' ); 1203 $class_admin->child( select => 1204 title => loc('Select'), 1205 description => loc('Modify and Create Classes'), 1206 path => '/Admin/Articles/Classes/', 1207 ); 1208 $class_admin->child( create => 1209 title => loc('Create'), 1210 description => loc('Modify and Create Custom Fields for Articles'), 1211 path => '/Admin/Articles/Classes/Modify.html?Create=1', 1212 ); 1213 1214 1215 my $cfs = $article_admin->child( 'custom-fields' => 1216 title => loc('Custom Fields'), 1217 path => '/Admin/CustomFields/index.html?'.$HTML::Mason::Commands::m->comp('/Elements/QueryString', Type => 'RT::Class-RT::Article'), 1218 ); 1219 $cfs->child( select => 1220 title => loc('Select'), 1221 path => '/Admin/CustomFields/index.html?'.$HTML::Mason::Commands::m->comp('/Elements/QueryString', Type => 'RT::Class-RT::Article'), 1222 ); 1223 $cfs->child( create => 1224 title => loc('Create'), 1225 path => '/Admin/CustomFields/Modify.html?'.$HTML::Mason::Commands::m->comp("/Elements/QueryString", Create=>1, LookupType=> "RT::Class-RT::Article" ), 1226 ); 1227 1228 my $assets_admin = $admin->child( assets => title => loc("Assets"), path => '/Admin/Assets/' ); 1229 my $catalog_admin = $assets_admin->child( catalogs => 1230 title => loc("Catalogs"), 1231 description => loc("Modify asset catalogs"), 1232 path => "/Admin/Assets/Catalogs/" 1233 ); 1234 $catalog_admin->child( "select", title => loc("Select"), path => $catalog_admin->path ); 1235 $catalog_admin->child( "create", title => loc("Create"), path => "Create.html" ); 1236 1237 1238 my $assets_cfs = $assets_admin->child( "cfs", 1239 title => loc("Custom Fields"), 1240 description => loc("Modify asset custom fields"), 1241 path => "/Admin/CustomFields/?Type=" . RT::Asset->CustomFieldLookupType 1242 ); 1243 $assets_cfs->child( "select", title => loc("Select"), path => $assets_cfs->path ); 1244 $assets_cfs->child( "create", title => loc("Create"), path => "/Admin/CustomFields/Modify.html?Create=1;LookupType=" . RT::Asset->CustomFieldLookupType); 1245 1246 $admin_global->child( 'group-rights' => 1247 title => loc('Group Rights'), 1248 description => loc('Modify global group rights'), 1249 path => '/Admin/Global/GroupRights.html', 1250 ); 1251 $admin_global->child( 'user-rights' => 1252 title => loc('User Rights'), 1253 description => loc('Modify global user rights'), 1254 path => '/Admin/Global/UserRights.html', 1255 ); 1256 $admin_global->child( 'my-rt' => 1257 title => loc('RT at a glance'), 1258 description => loc('Modify the default "RT at a glance" view'), 1259 path => '/Admin/Global/MyRT.html', 1260 ); 1261 1262 if (RT->Config->Get('SelfServiceUseDashboard')) { 1263 if ($current_user->HasRight( Right => 'ModifyDashboard', Object => RT->System ) ) { 1264 my $self_service = $admin_global->child( selfservice_home => 1265 title => loc('Self Service Home Page'), 1266 description => loc('Edit self service home page dashboard'), 1267 path => '/Admin/Global/SelfServiceHomePage.html'); 1268 if ( $request_path =~ m{^/Admin/Global/SelfServiceHomePage} ) { 1269 $page->child(content => title => loc('Content'), path => '/Admin/Global/SelfServiceHomePage.html'); 1270 $page->child(show => title => loc('Show'), path => '/SelfService'); 1271 } 1272 } 1273 } 1274 $admin_global->child( 'dashboards-in-menu' => 1275 title => loc('Modify Reports menu'), 1276 description => loc('Customize dashboards in menu'), 1277 path => '/Admin/Global/DashboardsInMenu.html', 1278 ); 1279 $admin_global->child( 'topics' => 1280 title => loc('Topics'), 1281 description => loc('Modify global article topics'), 1282 path => '/Admin/Global/Topics.html', 1283 ); 1284 1285 my $admin_tools = $admin->child( tools => 1286 title => loc('Tools'), 1287 description => loc('Use other RT administrative tools'), 1288 path => '/Admin/Tools/', 1289 ); 1290 $admin_tools->child( configuration => 1291 title => loc('System Configuration'), 1292 description => loc('Detailed information about your RT setup'), 1293 path => '/Admin/Tools/Configuration.html', 1294 ); 1295 $admin_tools->child( theme => 1296 title => loc('Theme'), 1297 description => loc('Customize the look of your RT'), 1298 path => '/Admin/Tools/Theme.html', 1299 ); 1300 if (RT->Config->Get('StatementLog') 1301 && $current_user->HasRight( Right => 'SuperUser', Object => RT->System )) { 1302 $admin_tools->child( 'sql-queries' => 1303 title => loc('SQL Queries'), 1304 description => loc('Browse the SQL queries made in this process'), 1305 path => '/Admin/Tools/Queries.html', 1306 ); 1307 } 1308 $admin_tools->child( rights_inspector => 1309 title => loc('Rights Inspector'), 1310 description => loc('Search your configured rights'), 1311 path => '/Admin/Tools/RightsInspector.html', 1312 ); 1313 $admin_tools->child( shredder => 1314 title => loc('Shredder'), 1315 description => loc('Permanently wipeout data from RT'), 1316 path => '/Admin/Tools/Shredder', 1317 ); 1318 1319 if ( RT->Config->Get('GnuPG')->{'Enable'} 1320 && $current_user->HasRight( Right => 'SuperUser', Object => RT->System ) ) 1321 { 1322 $admin_tools->child( 1323 'gnupg' => title => loc('Manage GnuPG Keys'), 1324 description => loc('Manage GnuPG keys'), 1325 path => '/Admin/Tools/GnuPG.html', 1326 ); 1327 } 1328 1329 if ( $request_path =~ m{^/Admin/(Queues|Users|Groups|CustomFields|CustomRoles)} ) { 1330 my $type = $1; 1331 1332 my %labels = ( 1333 Queues => loc("Queues"), 1334 Users => loc("Users"), 1335 Groups => loc("Groups"), 1336 CustomFields => loc("Custom Fields"), 1337 CustomRoles => loc("Custom Roles"), 1338 ); 1339 1340 my $section; 1341 if ( $request_path =~ m|^/Admin/$type/?(?:index.html)?$| 1342 || ( $request_path =~ m|^/Admin/$type/(?:Modify.html)$| 1343 && $HTML::Mason::Commands::DECODED_ARGS->{'Create'} ) 1344 ) 1345 { 1346 $section = $page; 1347 1348 } else { 1349 $section = $page->child( select => title => $labels{$type}, 1350 path => "/Admin/$type/" ); 1351 } 1352 1353 $section->child( select => title => loc('Select'), path => "/Admin/$type/" ); 1354 $section->child( create => title => loc('Create'), path => "/Admin/$type/Modify.html?Create=1" ); 1355 } 1356 1357 if ( $request_path =~ m{^/Admin/Queues} ) { 1358 if ( $HTML::Mason::Commands::DECODED_ARGS->{'id'} && $HTML::Mason::Commands::DECODED_ARGS->{'id'} =~ /^\d+$/ 1359 || 1360 $HTML::Mason::Commands::DECODED_ARGS->{'Queue'} && $HTML::Mason::Commands::DECODED_ARGS->{'Queue'} =~ /^\d+$/ 1361 ) { 1362 my $id = $HTML::Mason::Commands::DECODED_ARGS->{'Queue'} || $HTML::Mason::Commands::DECODED_ARGS->{'id'}; 1363 my $queue_obj = RT::Queue->new( $current_user ); 1364 $queue_obj->Load($id); 1365 1366 if ( $queue_obj and $queue_obj->id ) { 1367 my $queue = $page; 1368 $queue->child( basics => title => loc('Basics'), path => "/Admin/Queues/Modify.html?id=" . $id ); 1369 $queue->child( people => title => loc('Watchers'), path => "/Admin/Queues/People.html?id=" . $id ); 1370 1371 my $templates = $queue->child(templates => title => loc('Templates'), path => "/Admin/Queues/Templates.html?id=" . $id); 1372 $templates->child( select => title => loc('Select'), path => "/Admin/Queues/Templates.html?id=".$id); 1373 $templates->child( create => title => loc('Create'), path => "/Admin/Queues/Template.html?Create=1;Queue=".$id); 1374 1375 my $scrips = $queue->child( scrips => title => loc('Scrips'), path => "/Admin/Queues/Scrips.html?id=" . $id); 1376 $scrips->child( select => title => loc('Select'), path => "/Admin/Queues/Scrips.html?id=" . $id ); 1377 $scrips->child( create => title => loc('Create'), path => "/Admin/Scrips/Create.html?Queue=" . $id); 1378 1379 my $cfs = $queue->child( 'custom-fields' => title => loc('Custom Fields') ); 1380 my $ticket_cfs = $cfs->child( 'tickets' => title => loc('Tickets'), 1381 path => '/Admin/Queues/CustomFields.html?SubType=RT::Ticket;id=' . $id ); 1382 1383 my $txn_cfs = $cfs->child( 'transactions' => title => loc('Transactions'), 1384 path => '/Admin/Queues/CustomFields.html?SubType=RT::Ticket-RT::Transaction;id='.$id ); 1385 1386 $queue->child( 'custom-roles' => title => loc('Custom Roles'), path => "/Admin/Queues/CustomRoles.html?id=".$id ); 1387 $queue->child( 'group-rights' => title => loc('Group Rights'), path => "/Admin/Queues/GroupRights.html?id=".$id ); 1388 $queue->child( 'user-rights' => title => loc('User Rights'), path => "/Admin/Queues/UserRights.html?id=" . $id ); 1389 $queue->child( 'history' => title => loc('History'), path => "/Admin/Queues/History.html?id=" . $id ); 1390 $queue->child( 'default-values' => title => loc('Default Values'), path => "/Admin/Queues/DefaultValues.html?id=" . $id ); 1391 1392 # due to historical reasons of always having been in /Elements/Tabs 1393 $HTML::Mason::Commands::m->callback( CallbackName => 'PrivilegedQueue', queue_id => $id, page_menu => $queue, CallbackPage => '/Elements/Tabs' ); 1394 } 1395 } 1396 } 1397 if ( $request_path =~ m{^(/Admin/Users|/User/(Summary|History)\.html)} and $admin->child("users") ) { 1398 if ( $HTML::Mason::Commands::DECODED_ARGS->{'id'} && $HTML::Mason::Commands::DECODED_ARGS->{'id'} =~ /^\d+$/ ) { 1399 my $id = $HTML::Mason::Commands::DECODED_ARGS->{'id'}; 1400 my $obj = RT::User->new( $current_user ); 1401 $obj->Load($id); 1402 1403 if ( $obj and $obj->id ) { 1404 $page->child( basics => title => loc('Basics'), path => "/Admin/Users/Modify.html?id=" . $id ); 1405 $page->child( memberships => title => loc('Memberships'), path => "/Admin/Users/Memberships.html?id=" . $id ); 1406 $page->child( history => title => loc('History'), path => "/Admin/Users/History.html?id=" . $id ); 1407 $page->child( 'my-rt' => title => loc('RT at a glance'), path => "/Admin/Users/MyRT.html?id=" . $id ); 1408 $page->child( 'dashboards-in-menu' => 1409 title => loc('Modify Reports menu'), 1410 path => '/Admin/Users/DashboardsInMenu.html?id=' . $id, 1411 ); 1412 if ( RT->Config->Get('Crypt')->{'Enable'} ) { 1413 $page->child( keys => title => loc('Private keys'), path => "/Admin/Users/Keys.html?id=" . $id ); 1414 } 1415 $page->child( 'summary' => title => loc('User Summary'), path => "/User/Summary.html?id=" . $id ); 1416 1417 if ( $current_user->HasRight( Right => 'ManageAuthTokens', Object => RT->System ) ) { 1418 my $auth_tokens = $page->child( 1419 auth_tokens => title => loc('Auth Tokens'), 1420 path => '/Admin/Users/AuthTokens.html?id=' . $id 1421 ); 1422 1423 if ( $request_path =~ m{^/Admin/Users/AuthTokens\.html} ) { 1424 $auth_tokens->child( 1425 select_auth_token => title => loc('Select'), 1426 path => '/Admin/Users/AuthTokens.html?id=' . $id, 1427 ); 1428 $auth_tokens->child( 1429 create_auth_token => title => loc('Create'), 1430 raw_html => 1431 q[<a class="btn menu-item" href="#create-auth-token" data-toggle="modal" rel="modal:open">] 1432 . loc("Create") . "</a>" 1433 ); 1434 } 1435 } 1436 } 1437 } 1438 1439 } 1440 1441 if ( $request_path =~ m{^(/Admin/Groups|/Group/(Summary|History)\.html)} ) { 1442 if ( $HTML::Mason::Commands::DECODED_ARGS->{'id'} && $HTML::Mason::Commands::DECODED_ARGS->{'id'} =~ /^\d+$/ ) { 1443 my $id = $HTML::Mason::Commands::DECODED_ARGS->{'id'}; 1444 my $obj = RT::Group->new( $current_user ); 1445 $obj->Load($id); 1446 1447 if ( $obj and $obj->id ) { 1448 $page->child( basics => title => loc('Basics'), path => "/Admin/Groups/Modify.html?id=" . $obj->id ); 1449 $page->child( members => title => loc('Members'), path => "/Admin/Groups/Members.html?id=" . $obj->id ); 1450 $page->child( memberships => title => loc('Memberships'), path => "/Admin/Groups/Memberships.html?id=" . $obj->id ); 1451 $page->child( 'links' => 1452 title => loc("Links"), 1453 path => "/Admin/Groups/ModifyLinks.html?id=" . $obj->id, 1454 description => loc("Group links"), 1455 ); 1456 $page->child( 'group-rights' => title => loc('Group Rights'), path => "/Admin/Groups/GroupRights.html?id=" . $obj->id ); 1457 $page->child( 'user-rights' => title => loc('User Rights'), path => "/Admin/Groups/UserRights.html?id=" . $obj->id ); 1458 $page->child( history => title => loc('History'), path => "/Admin/Groups/History.html?id=" . $obj->id ); 1459 $page->child( 'summary' => 1460 title => loc("Group Summary"), 1461 path => "/Group/Summary.html?id=" . $obj->id, 1462 description => loc("Group summary page"), 1463 ); 1464 } 1465 } 1466 } 1467 1468 if ( $request_path =~ m{^/Admin/CustomFields/} ) { 1469 if ( $HTML::Mason::Commands::DECODED_ARGS->{'id'} && $HTML::Mason::Commands::DECODED_ARGS->{'id'} =~ /^\d+$/ ) { 1470 my $id = $HTML::Mason::Commands::DECODED_ARGS->{'id'}; 1471 my $obj = RT::CustomField->new( $current_user ); 1472 $obj->Load($id); 1473 1474 if ( $obj and $obj->id ) { 1475 $page->child( basics => title => loc('Basics'), path => "/Admin/CustomFields/Modify.html?id=".$id ); 1476 $page->child( 'group-rights' => title => loc('Group Rights'), path => "/Admin/CustomFields/GroupRights.html?id=" . $id ); 1477 $page->child( 'user-rights' => title => loc('User Rights'), path => "/Admin/CustomFields/UserRights.html?id=" . $id ); 1478 unless ( $obj->IsOnlyGlobal ) { 1479 $page->child( 'applies-to' => title => loc('Applies to'), path => "/Admin/CustomFields/Objects.html?id=" . $id ); 1480 } 1481 } 1482 } 1483 } 1484 1485 if ( $request_path =~ m{^/Admin/CustomRoles} ) { 1486 if ( $HTML::Mason::Commands::DECODED_ARGS->{'id'} && $HTML::Mason::Commands::DECODED_ARGS->{'id'} =~ /^\d+$/ ) { 1487 my $id = $HTML::Mason::Commands::DECODED_ARGS->{'id'}; 1488 my $obj = RT::CustomRole->new( $current_user ); 1489 $obj->Load($id); 1490 1491 if ( $obj and $obj->id ) { 1492 $page->child( basics => title => loc('Basics'), path => "/Admin/CustomRoles/Modify.html?id=".$id ); 1493 $page->child( 'applies-to' => title => loc('Applies to'), path => "/Admin/CustomRoles/Objects.html?id=" . $id ); 1494 $page->child( 'visibility' => title => loc('Visibility'), path => "/Admin/CustomRoles/Visibility.html?id=" . $id ); 1495 } 1496 } 1497 } 1498 1499 if ( $request_path =~ m{^/Admin/Scrips/} ) { 1500 if ( $HTML::Mason::Commands::m->request_args->{'id'} && $HTML::Mason::Commands::m->request_args->{'id'} =~ /^\d+$/ ) { 1501 my $id = $HTML::Mason::Commands::m->request_args->{'id'}; 1502 my $obj = RT::Scrip->new( $current_user ); 1503 $obj->Load($id); 1504 1505 my ( $admin_cat, $create_path_arg, $from_query_param ); 1506 my $from_arg = $HTML::Mason::Commands::DECODED_ARGS->{'From'} || q{}; 1507 my ($from_queue) = $from_arg =~ /^(\d+)$/; 1508 if ( $from_queue ) { 1509 $admin_cat = "Queues/Scrips.html?id=$from_queue"; 1510 $create_path_arg = "?Queue=$from_queue"; 1511 $from_query_param = ";From=$from_queue"; 1512 } 1513 elsif ( $from_arg eq 'Global' ) { 1514 $admin_cat = 'Global/Scrips.html'; 1515 $create_path_arg = '?Global=1'; 1516 $from_query_param = ';From=Global'; 1517 } 1518 else { 1519 $admin_cat = 'Scrips'; 1520 $from_query_param = $create_path_arg = q{}; 1521 } 1522 my $scrips = $page->child( scrips => title => loc('Scrips'), path => "/Admin/${admin_cat}" ); 1523 $scrips->child( select => title => loc('Select'), path => "/Admin/${admin_cat}" ); 1524 $scrips->child( create => title => loc('Create'), path => "/Admin/Scrips/Create.html${create_path_arg}" ); 1525 1526 $page->child( basics => title => loc('Basics') => path => "/Admin/Scrips/Modify.html?id=" . $id . $from_query_param ); 1527 $page->child( 'applies-to' => title => loc('Applies to'), path => "/Admin/Scrips/Objects.html?id=" . $id . $from_query_param ); 1528 } 1529 elsif ( $request_path =~ m{^/Admin/Scrips/(index\.html)?$} ) { 1530 HTML::Mason::Commands::PageMenu->child( select => title => loc('Select') => path => "/Admin/Scrips/" ); 1531 HTML::Mason::Commands::PageMenu->child( create => title => loc('Create') => path => "/Admin/Scrips/Create.html" ); 1532 } 1533 elsif ( $request_path =~ m{^/Admin/Scrips/Create\.html$} ) { 1534 my ($queue) = $HTML::Mason::Commands::DECODED_ARGS->{'Queue'} && $HTML::Mason::Commands::DECODED_ARGS->{'Queue'} =~ /^(\d+)$/; 1535 my $global_arg = $HTML::Mason::Commands::DECODED_ARGS->{'Global'}; 1536 if ($queue) { 1537 HTML::Mason::Commands::PageMenu->child( select => title => loc('Select') => path => "/Admin/Queues/Scrips.html?id=$queue" ); 1538 HTML::Mason::Commands::PageMenu->child( create => title => loc('Create') => path => "/Admin/Scrips/Create.html?Queue=$queue" ); 1539 } elsif ($global_arg) { 1540 HTML::Mason::Commands::PageMenu->child( select => title => loc('Select') => path => "/Admin/Global/Scrips.html" ); 1541 HTML::Mason::Commands::PageMenu->child( create => title => loc('Create') => path => "/Admin/Scrips/Create.html?Global=1" ); 1542 } else { 1543 HTML::Mason::Commands::PageMenu->child( select => title => loc('Select') => path => "/Admin/Scrips" ); 1544 HTML::Mason::Commands::PageMenu->child( create => title => loc('Create') => path => "/Admin/Scrips/Create.html" ); 1545 } 1546 } 1547 } 1548 1549 if ( $request_path =~ m{^/Admin/Lifecycles} && $current_user->HasRight( Object => RT->System, Right => 'SuperUser' ) ) { 1550 if (defined($HTML::Mason::Commands::DECODED_ARGS->{'Name'}) && defined($HTML::Mason::Commands::DECODED_ARGS->{'Type'}) ) { 1551 my $lifecycles = $page->child( 'lifecycles' => 1552 title => loc('Lifecycles'), 1553 description => loc('Manage lifecycles'), 1554 path => '/Admin/Lifecycles/', 1555 ); 1556 $lifecycles->child( select => title => loc('Select'), path => "/Admin/Lifecycles/" ); 1557 $lifecycles->child( create => title => loc('Create'), path => "/Admin/Lifecycles/Create.html" ); 1558 1559 my $LifecycleObj = RT::Lifecycle->new(); 1560 $LifecycleObj->Load(Name => $HTML::Mason::Commands::DECODED_ARGS->{'Name'}, Type => $HTML::Mason::Commands::DECODED_ARGS->{'Type'}); 1561 1562 if ($LifecycleObj->Name && $LifecycleObj->{data}{type} eq $HTML::Mason::Commands::DECODED_ARGS->{'Type'}) { 1563 my $Name_uri = $LifecycleObj->Name; 1564 my $Type_uri = $LifecycleObj->Type; 1565 RT::Interface::Web::EscapeURI(\$Name_uri); 1566 RT::Interface::Web::EscapeURI(\$Type_uri); 1567 1568 unless ( RT::Interface::Web->ClientIsIE ) { 1569 $page->child( basics => title => loc('Modify'), path => "/Admin/Lifecycles/Modify.html?Type=" . $Type_uri . ";Name=" . $Name_uri ); 1570 } 1571 $page->child( actions => title => loc('Actions'), path => "/Admin/Lifecycles/Actions.html?Type=" . $Type_uri . ";Name=" . $Name_uri ); 1572 $page->child( rights => title => loc('Rights'), path => "/Admin/Lifecycles/Rights.html?Type=" . $Type_uri . ";Name=" . $Name_uri ); 1573 $page->child( mappings => title => loc('Mappings'), path => "/Admin/Lifecycles/Mappings.html?Type=" . $Type_uri . ";Name=" . $Name_uri ); 1574 $page->child( advanced => title => loc('Advanced'), path => "/Admin/Lifecycles/Advanced.html?Type=" . $Type_uri . ";Name=" . $Name_uri ); 1575 } 1576 } 1577 else { 1578 $page->child( select => title => loc('Select'), path => "/Admin/Lifecycles/" ); 1579 $page->child( create => title => loc('Create'), path => "/Admin/Lifecycles/Create.html" ); 1580 } 1581 } 1582 1583 if ( $request_path =~ m{^/Admin/Global/Scrips\.html} ) { 1584 $page->child( select => title => loc('Select'), path => "/Admin/Global/Scrips.html" ); 1585 $page->child( create => title => loc('Create'), path => "/Admin/Scrips/Create.html?Global=1" ); 1586 } 1587 1588 if ( $request_path =~ m{^/Admin(?:/Global)?/Conditions} ) { 1589 $page->child( select => title => loc('Select'), path => "/Admin/Global/Conditions.html" ); 1590 $page->child( create => title => loc('Create'), path => "/Admin/Conditions/Create.html" ); 1591 } 1592 1593 if ( $request_path =~ m{^/Admin(?:/Global)?/Actions} ) { 1594 $page->child( select => title => loc('Select'), path => "/Admin/Global/Actions.html" ); 1595 $page->child( create => title => loc('Create'), path => "/Admin/Actions/Create.html" ); 1596 } 1597 1598 if ( $request_path =~ m{^/Admin/Global/Templates?\.html} ) { 1599 $page->child( select => title => loc('Select'), path => "/Admin/Global/Templates.html" ); 1600 $page->child( create => title => loc('Create'), path => "/Admin/Global/Template.html?Create=1" ); 1601 } 1602 1603 if ( $request_path =~ m{^/Admin/Articles/Classes/} ) { 1604 if ( my $id = $HTML::Mason::Commands::DECODED_ARGS->{'id'} ) { 1605 my $obj = RT::Class->new( $current_user ); 1606 $obj->Load($id); 1607 1608 if ( $obj and $obj->id ) { 1609 my $section = $page->child( select => title => loc("Classes"), path => "/Admin/Articles/Classes/" ); 1610 $section->child( select => title => loc('Select'), path => "/Admin/Articles/Classes/" ); 1611 $section->child( create => title => loc('Create'), path => "/Admin/Articles/Classes/Modify.html?Create=1" ); 1612 1613 $page->child( basics => title => loc('Basics'), path => "/Admin/Articles/Classes/Modify.html?id=".$id ); 1614 $page->child( topics => title => loc('Topics'), path => "/Admin/Articles/Classes/Topics.html?id=".$id ); 1615 $page->child( 'custom-fields' => title => loc('Custom Fields'), path => "/Admin/Articles/Classes/CustomFields.html?id=".$id ); 1616 $page->child( 'group-rights' => title => loc('Group Rights'), path => "/Admin/Articles/Classes/GroupRights.html?id=".$id ); 1617 $page->child( 'user-rights' => title => loc('User Rights'), path => "/Admin/Articles/Classes/UserRights.html?id=".$id ); 1618 $page->child( 'applies-to' => title => loc('Applies to'), path => "/Admin/Articles/Classes/Objects.html?id=$id" ); 1619 } 1620 } else { 1621 $page->child( select => title => loc('Select'), path => "/Admin/Articles/Classes/" ); 1622 $page->child( create => title => loc('Create'), path => "/Admin/Articles/Classes/Modify.html?Create=1" ); 1623 } 1624 } 1625} 1626 1627sub BuildSelfServiceNav { 1628 my $request_path = shift; 1629 my $top = shift; 1630 my $widgets = shift; 1631 my $page = shift; 1632 1633 my %args = ( @_ ); 1634 1635 my $current_user = $HTML::Mason::Commands::session{CurrentUser}; 1636 1637 if ( RT->Config->Get('SelfServiceUseDashboard') 1638 && $request_path =~ m{^/SelfService/(?:index\.html)?$} 1639 && $current_user->HasRight( 1640 Right => 'ShowConfigTab', 1641 Object => RT->System 1642 ) 1643 && $current_user->HasRight( Right => 'ModifyDashboard', Object => RT->System ) 1644 ) 1645 { 1646 $page->child( content => title => loc('Content'), path => '/Admin/Global/SelfServiceHomePage.html' ); 1647 $page->child( show => title => loc('Show'), path => '/SelfService/' ); 1648 } 1649 1650 my $queues = RT::Queues->new( $current_user ); 1651 $queues->UnLimit; 1652 1653 my $queue_count = 0; 1654 my $queue_id; 1655 1656 while ( my $queue = $queues->Next ) { 1657 next unless $queue->CurrentUserHasRight('CreateTicket'); 1658 $queue_id = $queue->id; 1659 $queue_count++; 1660 last if ( $queue_count > 1 ); 1661 } 1662 1663 my $home = $top->child( home => title => loc('Homepage'), path => '/' ); 1664 1665 if ( $queue_count > 1 ) { 1666 $home->child( new => title => loc('Create Ticket'), path => '/SelfService/CreateTicketInQueue.html' ); 1667 } elsif ( $queue_id ) { 1668 $home->child( new => title => loc('Create Ticket'), path => '/SelfService/Create.html?Queue=' . $queue_id ); 1669 } 1670 1671 my $menu_label = loc('Tickets'); 1672 my $menu_path = '/SelfService/'; 1673 if ( RT->Config->Get('SelfServiceUseDashboard') ) { 1674 $menu_path = '/SelfService/Open.html'; 1675 } 1676 my $tickets = $top->child( tickets => title => $menu_label, path => $menu_path ); 1677 $tickets->child( open => title => loc('Open tickets'), path => '/SelfService/Open.html' ); 1678 $tickets->child( closed => title => loc('Closed tickets'), path => '/SelfService/Closed.html' ); 1679 1680 $top->child( "assets", title => loc("Assets"), path => "/SelfService/Asset/" ) 1681 if $current_user->HasRight( Right => 'ShowAssetsMenu', Object => RT->System ); 1682 1683 my $username = '<span class="current-user">' 1684 . $HTML::Mason::Commands::m->interp->apply_escapes($current_user->Name, 'h') 1685 . '</span>'; 1686 my $about_me = $top->child( preferences => 1687 title => loc('Logged in as [_1]', $username), 1688 escape_title => 0, 1689 sort_order => 99, 1690 ); 1691 1692 if ( ( RT->Config->Get('SelfServiceUserPrefs') || '' ) eq 'view-info' || 1693 $current_user->HasRight( Right => 'ModifySelf', Object => RT->System ) ) { 1694 $about_me->child( prefs => title => loc('Preferences'), path => '/SelfService/Prefs.html' ); 1695 } 1696 1697 my $logout_url = RT->Config->Get('LogoutURL'); 1698 if ( $current_user->Name 1699 && ( !RT->Config->Get('WebRemoteUserAuth') 1700 || RT->Config->Get('WebFallbackToRTLogin') )) { 1701 $about_me->child( logout => title => loc('Logout'), path => $logout_url ); 1702 } 1703 1704 if ( RT->Config->Get('SelfServiceShowArticleSearch') ) { 1705 $widgets->child( 'goto-article' => raw_html => $HTML::Mason::Commands::m->scomp('/SelfService/Elements/SearchArticle') ); 1706 } 1707 1708 $widgets->child( goto => raw_html => $HTML::Mason::Commands::m->scomp('/SelfService/Elements/GotoTicket') ); 1709 1710 if ($request_path =~ m{^/SelfService/Asset/} and $HTML::Mason::Commands::DECODED_ARGS->{id}) { 1711 my $id = $HTML::Mason::Commands::DECODED_ARGS->{id}; 1712 $page->child("display", title => loc("Display"), path => "/SelfService/Asset/Display.html?id=$id"); 1713 $page->child("history", title => loc("History"), path => "/SelfService/Asset/History.html?id=$id"); 1714 1715 if (Menu->child("new")) { 1716 my $actions = $page->child("actions", title => loc("Actions")); 1717 $actions->child("create-linked-ticket", title => loc("Create linked ticket"), path => "/SelfService/Asset/CreateLinkedTicket.html?Asset=$id"); 1718 } 1719 } 1720 1721 # due to historical reasons of always having been in /Elements/Tabs 1722 $HTML::Mason::Commands::m->callback( CallbackName => 'SelfService', Path => $request_path, ARGSRef => \%args, CallbackPage => '/Elements/Tabs' ); 1723} 1724 17251; 1726