1 %# BEGIN BPS TAGGED BLOCK {{{
5 %# This software is Copyright (c) 1996-2015 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 }}}
48 <& /Elements/PageLayout, show_menu => $show_menu &>
49 <a name="skipnav" id="skipnav" accesskey="8"></a>
52 #my $request_path = $HTML::Mason::Commands::r->path_info;
53 my $request_path = $m->request_comp->path;
54 $request_path =~ s!/{2,}!/!g;
56 my $query_string = sub {
59 $u->query_form(map { $_ => $args{$_} } sort keys %args);
63 my $build_admin_menu = sub {
65 my $admin = $top->child( config => title => loc('Configuration'), path => '/Admin/', sort_order => 99 );
66 if ( $session{'CurrentUser'}->HasRight( Object => RT->System, Right => 'AdminUsers' ) ) {
67 my $users = $admin->child( users =>
68 title => loc('Users'),
69 description => loc('Manage users and passwords'),
70 path => '/Admin/Users/',
72 $users->child( select => title => loc('Select'), path => "/Admin/Users/" );
73 $users->child( create => title => loc('Create'), path => "/Admin/Users/Modify.html?Create=1" );
75 my $groups = $admin->child( groups =>
76 title => loc('Groups'),
77 description => loc('Manage groups and group membership'),
78 path => '/Admin/Groups/',
80 $groups->child( select => title => loc('Select'), path => "/Admin/Groups/" );
81 $groups->child( create => title => loc('Create'), path => "/Admin/Groups/Modify.html?Create=1" );
83 my $queues = $admin->child( queues =>
84 title => loc('Queues'),
85 description => loc('Manage queues and queue-specific properties'),
86 path => '/Admin/Queues/',
88 $queues->child( select => title => loc('Select'), path => "/Admin/Queues/" );
89 $queues->child( create => title => loc('Create'), path => "/Admin/Queues/Modify.html?Create=1" );
91 if ( $session{'CurrentUser'}->HasRight( Object => RT->System, Right => 'AdminCustomField' ) ) {
92 my $cfs = $admin->child( 'custom-fields' =>
93 title => loc('Custom Fields'),
94 description => loc('Manage custom fields and custom field values'),
95 path => '/Admin/CustomFields/',
97 $cfs->child( select => title => loc('Select'), path => "/Admin/CustomFields/" );
98 $cfs->child( create => title => loc('Create'), path => "/Admin/CustomFields/Modify.html?Create=1" );
101 my $admin_global = $admin->child( global =>
102 title => loc('Global'),
103 description => loc('Manage properties and configuration which apply to all queues'),
104 path => '/Admin/Global/',
107 my $scrips = $admin_global->child( scrips =>
108 title => loc('Scrips'),
109 description => loc('Modify scrips which apply to all queues'),
110 path => '/Admin/Global/Scrips.html',
112 $scrips->child( select => title => loc('Select'), path => "/Admin/Global/Scrips.html" );
113 $scrips->child( create => title => loc('Create'), path => "/Admin/Global/Scrip.html?Create=1" );
115 my $templates = $admin_global->child( templates =>
116 title => loc('Templates'),
117 description => loc('Edit system templates'),
118 path => '/Admin/Global/Templates.html',
120 $templates->child( select => title => loc('Select'), path => "/Admin/Global/Templates.html" );
121 $templates->child( create => title => loc('Create'), path => "/Admin/Global/Template.html?Create=1" );
123 my $cfadmin = $admin_global->child( 'custom-fields' =>
124 title => loc('Custom Fields'),
125 description => loc('Modify global custom fields'),
126 path => '/Admin/Global/CustomFields/index.html',
128 $cfadmin->child( users =>
129 title => loc('Users'),
130 description => loc('Select custom fields for all users'),
131 path => '/Admin/Global/CustomFields/Users.html',
133 $cfadmin->child( groups =>
134 title => loc('Groups'),
135 description => loc('Select custom fields for all user groups'),
136 path => '/Admin/Global/CustomFields/Groups.html',
138 $cfadmin->child( queues =>
139 title => loc('Queues'),
140 description => loc('Select custom fields for all queues'),
141 path => '/Admin/Global/CustomFields/Queues.html',
143 $cfadmin->child( tickets =>
144 title => loc('Tickets'),
145 description => loc('Select custom fields for tickets in all queues'),
146 path => '/Admin/Global/CustomFields/Queue-Tickets.html',
148 $cfadmin->child( transactions =>
149 title => loc('Ticket Transactions'),
150 description => loc('Select custom fields for transactions on tickets in all queues'),
151 path => '/Admin/Global/CustomFields/Queue-Transactions.html',
153 $cfadmin->child( 'custom-fields' =>
154 title => loc('Articles'),
155 description => loc('Select Custom Fields for Articles in all Classes'),
156 path => '/Admin/Global/CustomFields/Class-Article.html',
159 my $article_admin = $admin->child( articles => title => loc('Articles'), path => "/Admin/Articles/index.html" );
160 my $class_admin = $article_admin->child(classes => title => loc('Classes'), path => '/Admin/Articles/Classes/' );
161 $class_admin->child( select =>
162 title => loc('Select'),
163 description => loc('Modify and Create Classes'),
164 path => '/Admin/Articles/Classes/',
166 $class_admin->child( create =>
167 title => loc('Create'),
168 description => loc('Modify and Create Custom Fields for Articles'),
169 path => '/Admin/Articles/Classes/Modify.html?Create=1',
173 my $cfs = $article_admin->child( 'custom-fields' =>
174 title => loc('Custom Fields'),
175 path => '/Admin/CustomFields/index.html?'.$m->comp('/Elements/QueryString', type => 'RT::Class-RT::Article'),
177 $cfs->child( select =>
178 title => loc('Select'),
179 path => '/Admin/CustomFields/index.html?'.$m->comp('/Elements/QueryString', type => 'RT::Class-RT::Article'),
181 $cfs->child( create =>
182 title => loc('Create'),
183 path => '/Admin/CustomFields/Modify.html?'.$m->comp("/Elements/QueryString", Create=>1, LookupType=> "RT::Class-RT::Article" ),
186 $admin_global->child( 'group-rights' =>
187 title => loc('Group Rights'),
188 description => loc('Modify global group rights'),
189 path => '/Admin/Global/GroupRights.html',
191 $admin_global->child( 'user-rights' =>
192 title => loc('User Rights'),
193 description => loc('Modify global user rights'),
194 path => '/Admin/Global/UserRights.html',
196 $admin_global->child( 'my-rt' =>
197 title => loc('RT at a glance'),
198 description => loc('Modify the default "RT at a glance" view'),
199 path => '/Admin/Global/MyRT.html',
201 $admin_global->child( 'topics' =>
202 title => loc('Topics'),
203 description => loc('Modify global article topics'),
204 path => '/Admin/Global/Topics.html',
207 my $admin_tools = $admin->child( tools =>
208 title => loc('Tools'),
209 description => loc('Use other RT administrative tools'),
210 path => '/Admin/Tools/',
212 $admin_tools->child( configuration =>
213 title => loc('System Configuration'),
214 description => loc('Detailed information about your RT setup'),
215 path => '/Admin/Tools/Configuration.html',
217 $admin_tools->child( theme =>
218 title => loc('Theme'),
219 description => loc('Customize the look of your RT'),
220 path => '/Admin/Tools/Theme.html',
222 if (RT->Config->Get('StatementLog')
223 && $session{'CurrentUser'}->HasRight( Right => 'SuperUser', Object => RT->System )) {
224 $admin_tools->child( 'sql-queries' =>
225 title => loc('SQL Queries'),
226 description => loc('Browse the SQL queries made in this process'),
227 path => '/Admin/Tools/Queries.html',
230 $admin_tools->child( shredder =>
231 title => loc('Shredder'),
232 description => loc('Permanently wipeout data from RT'),
233 path => '/Admin/Tools/Shredder',
236 if ( $request_path =~ m{^/Admin/(Queues|Users|Groups|CustomFields)} ) {
238 my $tabs = PageMenu();
241 Queues => loc("Queues"),
242 Users => loc("Users"),
243 Groups => loc("Groups"),
244 CustomFields => loc("Custom Fields"),
248 if ( $request_path =~ m|^/Admin/$type/?(?:index.html)?$|
249 || ( $request_path =~ m|^/Admin/$type/(?:Modify.html)$|
250 && $DECODED_ARGS->{'Create'} )
256 $section = $tabs->child( select => title => $labels{$type},
257 path => "/Admin/$type/" );
260 $section->child( select => title => loc('Select'), path => "/Admin/$type/" );
261 $section->child( create => title => loc('Create'), path => "/Admin/$type/Modify.html?Create=1" );
264 if ( $request_path =~ m{^/Admin/Queues} ) {
265 if ( $DECODED_ARGS->{'id'} && $DECODED_ARGS->{'id'} =~ /^\d+$/
267 $DECODED_ARGS->{'Queue'} && $DECODED_ARGS->{'Queue'} =~ /^\d+$/
269 my $id = $DECODED_ARGS->{'Queue'} || $DECODED_ARGS->{'id'};
270 my $queue_obj = RT::Queue->new( $session{'CurrentUser'} );
271 $queue_obj->Load($id);
273 if ( $queue_obj and $queue_obj->id ) {
274 my $queue = PageMenu();
275 $queue->child( basics => title => loc('Basics'), path => "/Admin/Queues/Modify.html?id=" . $id );
276 $queue->child( people => title => loc('Watchers'), path => "/Admin/Queues/People.html?id=" . $id );
278 my $templates = $queue->child(templates => title => loc('Templates'), path => "/Admin/Queues/Templates.html?id=" . $id);
279 $templates->child( select => title => loc('Select'), path => "/Admin/Queues/Templates.html?id=".$id);
280 $templates->child( create => title => loc('Create'), path => "/Admin/Queues/Template.html?Create=1;Queue=".$id);
282 my $scrips = $queue->child( scrips => title => loc('Scrips'), path => "/Admin/Queues/Scrips.html?id=" . $id);
283 $scrips->child( select => title => loc('Select'), path => "/Admin/Queues/Scrips.html?id=" . $id );
284 $scrips->child( create => title => loc('Create'), path => "/Admin/Queues/Scrip.html?Create=1;Queue=" . $id);
286 my $ticket_cfs = $queue->child( 'ticket-custom-fields' => title => loc('Ticket Custom Fields'),
287 path => '/Admin/Queues/CustomFields.html?SubType=RT::Ticket&id=' . $id );
289 my $txn_cfs = $queue->child( 'transaction-custom-fields' => title => loc('Transaction Custom Fields'),
290 path => '/Admin/Queues/CustomFields.html?SubType=RT::Ticket-RT::Transaction&id='.$id );
292 $queue->child( 'tasks' => title => loc('Subtasks'), path => "Admin/Queues/Tasks.html?Queue=".$id );
294 $queue->child( 'group-rights' => title => loc('Group Rights'), path => "/Admin/Queues/GroupRights.html?id=".$id );
295 $queue->child( 'user-rights' => title => loc('User Rights'), path => "/Admin/Queues/UserRights.html?id=" . $id );
296 $queue->child( 'history' => title => loc('History'), path => "/Admin/Queues/History.html?id=" . $id );
298 $m->callback( CallbackName => 'PrivilegedQueue', queue_id => $id, page_menu => $queue);
302 if ( $request_path =~ m{^/Admin/Users} ) {
303 if ( $DECODED_ARGS->{'id'} && $DECODED_ARGS->{'id'} =~ /^\d+$/ ) {
304 my $id = $DECODED_ARGS->{'id'};
305 my $obj = RT::User->new( $session{'CurrentUser'} );
308 if ( $obj and $obj->id ) {
309 my $tabs = PageMenu();
310 $tabs->child( basics => title => loc('Basics'), path => "/Admin/Users/Modify.html?id=" . $id );
311 $tabs->child( memberships => title => loc('Memberships'), path => "/Admin/Users/Memberships.html?id=" . $id );
312 $tabs->child( history => title => loc('History'), path => "/Admin/Users/History.html?id=" . $id );
313 $tabs->child( 'my-rt' => title => loc('RT at a glance'), path => "/Admin/Users/MyRT.html?id=" . $id );
314 if ( RT->Config->Get('GnuPG')->{'Enable'} ) {
315 $tabs->child( pgp => title => loc('GnuPG'), path => "/Admin/Users/GnuPG.html?id=" . $id );
322 if ( $request_path =~ m{^/Admin/Groups} ) {
323 if ( $DECODED_ARGS->{'id'} && $DECODED_ARGS->{'id'} =~ /^\d+$/ ) {
324 my $id = $DECODED_ARGS->{'id'};
325 my $obj = RT::Group->new( $session{'CurrentUser'} );
328 if ( $obj and $obj->id ) {
329 my $tabs = PageMenu();
330 $tabs->child( basics => title => loc('Basics'), path => "/Admin/Groups/Modify.html?id=" . $obj->id );
331 $tabs->child( members => title => loc('Members'), path => "/Admin/Groups/Members.html?id=" . $obj->id );
332 $tabs->child( 'group-rights' => title => loc('Group Rights'), path => "/Admin/Groups/GroupRights.html?id=" . $obj->id );
333 $tabs->child( 'user-rights' => title => loc('User Rights'), path => "/Admin/Groups/UserRights.html?id=" . $obj->id );
334 $tabs->child( history => title => loc('History'), path => "/Admin/Groups/History.html?id=" . $obj->id );
339 if ( $request_path =~ m{^/Admin/CustomFields/} ) {
340 if ( $DECODED_ARGS->{'id'} && $DECODED_ARGS->{'id'} =~ /^\d+$/ ) {
341 my $id = $DECODED_ARGS->{'id'};
342 my $obj = RT::CustomField->new( $session{'CurrentUser'} );
345 if ( $obj and $obj->id ) {
346 my $tabs = PageMenu();
347 $tabs->child( basics => title => loc('Basics'), path => "/Admin/CustomFields/Modify.html?id=".$id );
348 $tabs->child( 'group-rights' => title => loc('Group Rights'), path => "/Admin/CustomFields/GroupRights.html?id=" . $id );
349 $tabs->child( 'user-rights' => title => loc('User Rights'), path => "/Admin/CustomFields/UserRights.html?id=" . $id );
350 $tabs->child( 'applies-to' => title => loc('Applies to'), path => "/Admin/CustomFields/Objects.html?id=" . $id );
355 if ( $request_path =~ m{^/Admin/Global/(Scrip|Template)s?\.html} ) {
357 my $tabs = PageMenu();
359 # With only two elements, swapping between dropdown and menu is kinda dumb
360 # In the glorious future this should be cleaner.
362 $tabs->child( select => title => loc('Select'), path => "/Admin/Global/${type}s.html" );
363 $tabs->child( create => title => loc('Create'), path => "/Admin/Global/${type}.html?Create=1" );
366 if ( $request_path =~ m{^/Admin/Articles/Classes/} ) {
367 my $tabs = PageMenu();
368 if ( my $id = $DECODED_ARGS->{'id'} ) {
369 my $obj = RT::Class->new( $session{'CurrentUser'} );
372 if ( $obj and $obj->id ) {
373 my $section = $tabs->child( select => title => loc("Classes"), path => "/Admin/Articles/Classes/" );
374 $section->child( select => title => loc('Select'), path => "/Admin/Articles/Classes/" );
375 $section->child( create => title => loc('Create'), path => "/Admin/Articles/Classes/Modify.html?Create=1" );
377 $tabs->child( basics => title => loc('Basics'), path => "/Admin/Articles/Classes/Modify.html?id=".$id );
378 $tabs->child( topics => title => loc('Topics'), path => "/Admin/Articles/Classes/Topics.html?id=".$id );
379 $tabs->child( 'custom-fields' => title => loc('Custom Fields'), path => "/Admin/Articles/Classes/CustomFields.html?id=".$id );
380 $tabs->child( 'group-rights' => title => loc('Group Rights'), path => "/Admin/Articles/Classes/GroupRights.html?id=".$id );
381 $tabs->child( 'user-rights' => title => loc('User Rights'), path => "/Admin/Articles/Classes/UserRights.html?id=".$id );
382 $tabs->child( 'applies-to' => title => loc('Applies to'), path => "/Admin/Articles/Classes/Objects.html?id=$id" );
385 $tabs->child( select => title => loc('Select'), path => "/Admin/Articles/Classes/" );
386 $tabs->child( create => title => loc('Create'), path => "/Admin/Articles/Classes/Modify.html?Create=1" );
392 my $build_main_nav = sub {
394 PageWidgets()->child( simple_search => raw_html => $m->scomp('SimpleSearch') );
395 PageWidgets()->child( create_ticket => raw_html => $m->scomp('CreateTicket') );
397 my $home = Menu->child( home => title => loc('Homepage'), path => '/' );
398 # We explicitly exclude superusers; otherwise the dashboards for
399 # groups you're not in (but can see the dashboards of by dint of
400 # being a superuser) would push the useful ones from the groups
401 # you're actually in off of the stack.
402 my @dashboards = $m->comp("/Dashboards/Elements/ListOfDashboards", IncludeSuperuserGroups => 0);
406 if ( @dashboards > $limit ) {
408 splice @dashboards, $limit;
411 my $dashes = Menu()->child('home');
413 for my $dash (@dashboards) {
414 $home->child( 'dashboard-' . $dash->id,
415 title => $dash->Name,
416 path => '/Dashboards/' . $dash->id . '/' . $dash->Name
420 $dashes->child( more => title => loc('All Dashboards'), path => 'Dashboards/index.html' );
422 my $dashboard = RT::Dashboard->new( $session{CurrentUser} );
423 if ( $dashboard->CurrentUserCanCreateAny ) {
424 $dashes->child('dashboard_create' => title => loc('New Dashboard'), path => "/Dashboards/Modify.html?Create=1" );
427 my $tickets = Menu->child( search => title => loc('Tickets'), path => '/Search/Build.html' );
428 $tickets->child( simple => title => loc('Simple Search'), path => "/Search/Simple.html" );
429 $tickets->child( new => title => loc('New Search'), path => "/Search/Build.html?NewQuery=1" );
432 my $tools = Menu->child( tools => title => loc('Tools'), path => '/Tools/index.html' );
433 my $articles = $tools->child( articles => title => loc('Articles'), path => "/Articles/index.html");
434 $articles->child( articles => title => loc('Overview'), path => "/Articles/index.html" );
435 $articles->child( search => title => loc('Search'), path => "/Articles/Article/Search.html" );
436 $articles->child( topics => title => loc('Topics'), path => "/Articles/Topics.html" );
438 $tools->child( my_day =>
439 title => loc('My Day'),
440 description => loc('Easy updating of your open tickets'),
441 path => '/Tools/MyDay.html',
444 if ( RT->Config->Get('EnableReminders') ) {
445 $tools->child( my_reminders =>
446 title => loc('My Reminders'),
447 description => loc('Easy viewing of your reminders'),
448 path => '/Tools/MyReminders.html',
452 $tools->child( offline =>
453 title => loc('Offline'),
454 description => loc('Create tickets offline'),
455 path => '/Tools/Offline.html',
458 if ( $session{'CurrentUser'}->HasRight( Right => 'ShowApprovalsTab', Object => RT->System ) ) {
459 $tools->child( approval =>
460 title => loc('Approval'),
461 description => loc('My Approvals'),
462 path => '/Approvals/',
466 if ( $session{'CurrentUser'}->HasRight( Right => 'ShowConfigTab', Object => RT->System ) )
468 $build_admin_menu->($tools);
471 my $username = '<span class="current-user">'
472 . $m->interp->apply_escapes($session{'CurrentUser'}->Name, 'h')
474 my $about_me = Menu->child( 'preferences' =>
475 title => loc('Logged in as [_1]', $username),
481 if ( $session{'CurrentUser'}->UserObj
482 && $session{'CurrentUser'}->HasRight( Right => 'ModifySelf', Object => RT->System )) {
483 my $settings = $about_me->child( settings => title => loc('Settings'), path => '/Prefs/Other.html' );
484 $settings->child( options => title => loc('Options'), path => '/Prefs/Other.html' );
485 $settings->child( about_me => title => loc('About me'), path => '/User/Prefs.html' );
486 $settings->child( search_options => title => loc('Search options'), path => '/Prefs/SearchOptions.html' );
487 $settings->child( myrt => title => loc('RT at a glance'), path => '/Prefs/MyRT.html' );
488 $settings->child( quicksearch => title => loc('Quick search'), path => '/Prefs/Quicksearch.html' );
490 my $search_menu = $settings->child( 'saved-searches' => title => loc('Saved Searches') );
491 my $searches = [ $m->comp( "/Search/Elements/SearchesForObject",
492 Object => RT::System->new( $session{'CurrentUser'} )) ];
495 for my $search (@$searches) {
496 $search_menu->child( "search-" . $i++ =>
497 title => $search->[1],
498 path => "/Prefs/Search.html?"
499 . $query_string->( name => ref( $search->[2] ) . '-' . $search->[2]->Id ),
504 if ( $session{'CurrentUser'}->Name
505 && ( !RT->Config->Get('WebExternalAuth')
506 || RT->Config->Get('WebFallbackToInternalAuth') )) {
507 $about_me->child( logout => title => loc('Logout'), path => '/NoAuth/Logout.html' );
509 if ( $request_path =~ m{^/Dashboards/(\d+)?}) {
510 if ( my $id = ( $1 || $DECODED_ARGS->{'id'} ) ) {
511 my $obj = RT::Dashboard->new( $session{'CurrentUser'} );
513 if ( $obj and $obj->id ) {
515 $tabs->child( basics => title => loc('Basics'), path => "/Dashboards/Modify.html?id=" . $obj->id);
516 $tabs->child( content => title => loc('Content'), path => "/Dashboards/Queries.html?id=" . $obj->id);
517 $tabs->child( subscription => title => loc('Subscription'), path => "/Dashboards/Subscription.html?id=" . $obj->id)
518 if $obj->CurrentUserCanSubscribe;
519 $tabs->child( show => title => loc('Show'), path => "/Dashboards/" . $obj->id . "/" . $obj->Name)
522 my $tabs = PageMenu();
523 if ( $dashboard->CurrentUserCanCreateAny ) {
524 # FREESIDE: provide a way to create dashboards
525 $tabs->child('dashboard_create' => title => loc('New Dashboard'), path => "/Dashboards/Modify.html?Create=1");
530 if ( $request_path =~ m{^/Ticket/} ) {
531 if ( ( $DECODED_ARGS->{'id'} || '' ) =~ /^(\d+)$/ ) {
533 my $obj = RT::Ticket->new( $session{'CurrentUser'} );
536 if ( $obj and $obj->id ) {
537 my $actions = PageMenu()->child( actions => title => loc('Actions'), sort_order => 95 );
538 my $tabs = PageMenu();
539 $tabs->child( bookmark => raw_html => $m->scomp( '/Ticket/Elements/Bookmark', id => $id ), sort_order => 99 );
540 $tabs->child( display => title => loc('Display'), path => "/Ticket/Display.html?id=" . $id );
541 $tabs->child( history => title => loc('History'), path => "/Ticket/History.html?id=" . $id );
543 my %can = %{ $obj->CurrentUser->PrincipalObj->HasRights( Object => $obj ) };
544 $can{'_ModifyOwner'} = $can{'OwnTicket'} || $can{'TakeTicket'} || $can{'StealTicket'};
546 unless ($_[0] eq 'ExecuteCode') {
547 return $can{$_[0]} || $can{'SuperUser'};
549 return !RT->Config->Get('DisallowExecuteCode')
550 && ( $can{'ExecuteCode'} || $can{'SuperUser'} );
554 # comment out until we can do it for an individual custom field
555 #if ( $can->('ModifyTicket') || $can->('ModifyCustomField') ) {
556 $tabs->child( basics => title => loc('Basics'), path => "/Ticket/Modify.html?id=" . $id );
561 $tabs->child( customers => title => loc('Customers'), path => "/Ticket/ModifyCustomers.html?id=" . $id );
563 if ( $can->('ModifyTicket') || $can->('_ModifyOwner') || $can->('Watch') || $can->('WatchAsAdminCc') ) {
564 $tabs->child( people => title => loc('People'), path => "/Ticket/ModifyPeople.html?id=" . $id );
567 if ( $can->('ModifyTicket') ) {
568 $tabs->child( dates => title => loc('Dates'), path => "/Ticket/ModifyDates.html?id=" . $id );
569 $tabs->child( links => title => loc('Links'), path => "/Ticket/ModifyLinks.html?id=" . $id );
572 #if ( $can->('ModifyTicket') || $can->('ModifyCustomField') || $can->('_ModifyOwner') ) {
573 $tabs->child( jumbo => title => loc('Jumbo'), path => "/Ticket/ModifyAll.html?id=" . $id );
576 if ( RT->Config->Get('EnableReminders') ) {
577 $tabs->child( reminders => title => loc('Reminders'), path => "/Ticket/Reminders.html?id=" . $id );
580 if ( $can->('ModifyTicket') or $can->('ReplyToTicket') ) {
581 $actions->child( reply => title => loc('Reply'), path => "/Ticket/Update.html?Action=Respond;id=" . $id );
584 if ( $can->('ModifyTicket') or $can->('CommentOnTicket') ) {
585 $actions->child( comment => title => loc('Comment'), path => "/Ticket/Update.html?Action=Comment;id=" . $id );
588 if ( $can->('ForwardMessage') ) {
589 $actions->child( forward => title => loc('Forward'), path => "/Ticket/Forward.html?id=" . $id );
592 my $hide_resolve_with_deps = RT->Config->Get('HideResolveActionsWithDependencies')
593 && $obj->HasUnresolvedDependencies;
595 my $current = $obj->Status;
596 my $lifecycle = $obj->QueueObj->Lifecycle;
598 foreach my $info ( $lifecycle->Actions($current) ) {
599 my $next = $info->{'to'};
600 next unless $lifecycle->IsTransition( $current => $next );
602 my $check = $lifecycle->CheckRight( $current => $next );
603 next unless $can->($check);
605 next if $hide_resolve_with_deps
606 && $lifecycle->IsInactive($next)
607 && !$lifecycle->IsInactive($current);
609 my $action = $info->{'update'} || '';
610 my $url = '/Ticket/';
612 $url .= "Update.html?"
615 DefaultStatus => $next,
619 $url .= "Display.html?"
625 my $key = $info->{'label'} || ucfirst($next);
626 $actions->child( $key => title => loc( $key ), path => $url);
629 if ( $can->('OwnTicket') ) {
630 if ( $obj->OwnerObj->Id == RT->Nobody->id
631 && ( $can->('ModifyTicket') or $can->('TakeTicket') ) ) {
632 $actions->child( take => title => loc('Take'), path => "/Ticket/Display.html?Action=Take;id=" . $id );
635 elsif ( $obj->OwnerObj->id != RT->Nobody->id
636 && $obj->OwnerObj->id != $session{CurrentUser}->id
637 && ( $can->('ModifyTicket') or $can->('StealTicket') ) ) {
638 $actions->child( steal => title => loc('Steal'), path => "/Ticket/Display.html?Action=Steal;id=" . $id );
642 # TODO needs a "Can extract article into a class applied to this queue" check
643 $actions->child( 'extract-article' =>
644 title => loc('Extract Article'),
645 path => "/Articles/Article/ExtractIntoClass.html?Ticket=".$obj->id,
648 if ( defined $session{"tickets"} ) {
649 # we have to update session data if we get new ItemMap
650 my $updatesession = 1 unless ( $session{"tickets"}->{'item_map'} );
652 my $item_map = $session{"tickets"}->ItemMap;
654 if ($updatesession) {
655 $session{"tickets"}->PrepForSerialization();
658 my $search = Menu()->child('search');
659 # Don't display prev links if we're on the first ticket
660 if ( $item_map->{$id}->{prev} ) {
661 $search->child( first =>
662 title => '<< ' . loc('First'), class => "nav", path => "/Ticket/Display.html?id=" . $item_map->{first});
663 $search->child( prev =>
664 title => '< ' . loc('Prev'), class => "nav", path => "/Ticket/Display.html?id=" . $item_map->{$id}->{prev});
666 # Don't display next links if we're on the last ticket
667 if ( $item_map->{$id}->{next} ) {
668 $search->child( next =>
669 title => loc('Next') . ' >', class => "nav", path => "/Ticket/Display.html?id=" . $item_map->{$id}->{next});
670 if ( $item_map->{last} ) {
671 $search->child( last =>
672 title => loc('Last') . ' >>', class => "nav", path => "/Ticket/Display.html?id=" . $item_map->{last});
682 $request_path =~ m{^/(?:Ticket|Search)/}
683 && $request_path !~ m{^/Search/Simple\.html}
685 || ( $request_path =~ m{^/Search/Simple\.html}
686 && $DECODED_ARGS->{'q'} )
689 my $search = Menu()->child('search');
692 my $current_search = $session{"CurrentSearchHash"} || {};
693 my $search_id = $DECODED_ARGS->{'SavedSearchLoad'} || $DECODED_ARGS->{'SavedSearchId'} || $current_search->{'SearchId'} || '';
694 my $chart_id = $DECODED_ARGS->{'SavedChartSearchId'} || $current_search->{SavedChartSearchId};
696 $has_query = 1 if ( $DECODED_ARGS->{'Query'} or $current_search->{'Query'} );
699 my %fallback_query_args = (
700 SavedSearchId => ( $search_id eq 'new' ) ? undef : $search_id,
701 SavedChartSearchId => $chart_id,
705 $p => $DECODED_ARGS->{$p} || $current_search->{$p}
706 } qw(Query Format OrderBy Order Page)
709 defined $DECODED_ARGS->{'RowsPerPage'}
710 ? $DECODED_ARGS->{'RowsPerPage'}
711 : $current_search->{'RowsPerPage'}
716 $args = '?' . $QueryString;
719 my %final_query_args = ();
720 # key => callback to avoid unnecessary work
722 for my $param (keys %fallback_query_args) {
723 $final_query_args{$param} = defined($QueryArgs->{$param})
724 ? $QueryArgs->{$param}
725 : $fallback_query_args{$param};
728 for my $field (qw(Order OrderBy)) {
729 if ( ref( $final_query_args{$field} ) eq 'ARRAY' ) {
730 $final_query_args{$field} = join( "|", @{ $final_query_args{$field} } );
731 } elsif (not defined $final_query_args{$field}) {
732 delete $final_query_args{$field};
735 $final_query_args{$field} ||= '';
739 $args = '?' . $query_string->(%final_query_args);
742 my $current_search_menu;
743 if ( $request_path =~ m{^/Ticket} ) {
744 $current_search_menu = $search->child( current_search => title => loc('Current Search') );
745 $current_search_menu->path("/Search/Results.html$args") if $has_query;
747 $current_search_menu = PageMenu();
750 $current_search_menu->child( edit_search =>
751 title => loc('Edit Search'), path => "/Search/Build.html" . ( ($has_query) ? $args : '' ) );
752 $current_search_menu->child( advanced =>
753 title => loc('Advanced'), path => "/Search/Edit.html$args" );
755 $current_search_menu->child( results => title => loc('Show Results'), path => "/Search/Results.html$args" );
759 $current_search_menu->child( bulk => title => loc('Bulk Update'), path => "/Search/Bulk.html$args" );
760 $current_search_menu->child( chart => title => loc('Chart'), path => "/Search/Chart.html$args" );
762 #formerly Callbacks/RTx-Calendar/Ticket/Element/Tabs/Default
763 $current_search_menu->child( calendar => title => loc('Calendar'), path => "/Search/Calendar.html$args" );
765 my $more = $current_search_menu->child( more => title => loc('Feeds') );
767 $more->child( tsv => title => loc('TSV'), path => "/Search/Results.tsv$args" );
768 $more->child( csv => title => loc('CSV'), path => "/Search/Results.csv$args" );
769 $more->child( spreadsheet => title => loc('Spreadsheet'), path => "/Search/Results.xls$args" );
772 $_ => $QueryArgs->{$_} || $fallback_query_args{$_} || '' }
773 qw(Query Order OrderBy);
774 my $RSSQueryString = "?"
775 . $query_string->( Query => $rss_data{Query},
776 Order => $rss_data{Order},
777 OrderBy => $rss_data{OrderBy}
779 my $RSSPath = join '/', map $m->interp->apply_escapes( $_, 'u' ),
780 $session{'CurrentUser'}->UserObj->Name,
781 $session{'CurrentUser'}
782 ->UserObj->GenerateAuthString( $rss_data{Query}
784 . $rss_data{OrderBy} );
786 $more->child( rss => title => loc('RSS'), path => "/NoAuth/rss/$RSSPath/$RSSQueryString");
787 my $ical_path = join '/', map $m->interp->apply_escapes($_, 'u'),
788 $session{'CurrentUser'}->UserObj->Name,
789 $session{'CurrentUser'}->UserObj->GenerateAuthString( $rss_data{Query} ),
791 $more->child( ical => title => loc('iCal'), path => '/NoAuth/iCal/'.$ical_path);
793 if ($request_path =~ m{^/Search/Results.html}
794 && #XXX TODO better abstraction
795 $session{'CurrentUser'}->HasRight( Right => 'SuperUser', Object => RT->System )) {
796 my $shred_args = $query_string->(
799 'Tickets:query' => $rss_data{'Query'},
800 'Tickets:limit' => $QueryArgs->{'Rows'},
803 $more->child( shredder => title => loc('Shredder'), path => '/Admin/Tools/Shredder/?' . $shred_args);
809 if ( $request_path =~ m{^/Article/} ) {
810 if ( $DECODED_ARGS->{'id'} && $DECODED_ARGS->{'id'} =~ /^\d+$/ ) {
811 my $id = $DECODED_ARGS->{'id'};
812 my $tabs = PageMenu();
814 $tabs->child( display => title => loc('Display'), path => "/Articles/Article/Display.html?id=".$id );
815 $tabs->child( history => title => loc('History'), path => "/Articles/Article/History.html?id=".$id );
816 $tabs->child( modify => title => loc('Modify'), path => "/Articles/Article/Edit.html?id=".$id );
817 $tabs->child( delete => title => loc('Delete'), path => "/Articles/Article/Delete.html?id=".$id );
821 if ( $request_path =~ m{^/Articles/} ) {
822 PageWidgets()->child( article_search => raw_html => $m->scomp('/Articles/Elements/GotoArticle') );
823 PageWidgets()->delete('create_ticket');
824 PageWidgets()->delete('simple_search');
826 my $tabs = PageMenu();
827 $tabs->child( search => title => loc("Search"), path => "/Articles/Article/Search.html" );
828 $tabs->child( create => title => loc("New Article" ), path => "/Articles/Article/PreCreate.html" );
829 if ( $request_path =~ m{^/Articles/Article/} and ( $DECODED_ARGS->{'id'} || '' ) =~ /^(\d+)$/ ) {
831 my $obj = RT::Article->new( $session{'CurrentUser'} );
834 if ( $obj and $obj->id ) {
835 $tabs->child( display => title => loc("Display"), path => "/Articles/Article/Display.html?id=" . $id );
836 $tabs->child( history => title => loc('History'), path => '/Articles/Article/History.html?id=' . $id );
838 if ( $obj->CurrentUserHasRight('ModifyArticle') ) {
839 $tabs->child(modify => title => loc('Modify'), path => '/Articles/Article/Edit.html?id=' . $id );
841 if ( $obj->CurrentUserHasRight('DeleteArticle') ) {
842 $tabs->child(delete => title => loc('Delete'), path => '/Articles/Article/Delete.html?id=' . $id );
849 if ( $request_path =~ /^\/(?:index.html|$)/ ) {
850 PageMenu()->child( edit => title => loc('Edit'), path => '/Prefs/MyRT.html' );
853 $m->callback( CallbackName => 'Privileged' );
856 my $build_selfservice_nav = sub {
857 my $queues = RT::Queues->new( $session{'CurrentUser'} );
863 while ( my $queue = $queues->Next ) {
864 next unless $queue->CurrentUserHasRight('CreateTicket');
865 $queue_id = $queue->id;
867 last if ( $queue_count > 1 );
871 if ( $queue_count > 1 ) {
872 Menu->child( new => title => loc('New ticket'), path => '/SelfService/CreateTicketInQueue.html' );
873 } elsif ( $queue_id ) {
874 Menu->child( new => title => loc('New ticket'), path => '/SelfService/Create.html?Queue=' . $queue_id );
876 my $tickets = Menu->child( tickets => title => loc('Tickets'), path => '/SelfService/' );
877 $tickets->child( open => title => loc('Open tickets'), path => '/SelfService/' );
878 $tickets->child( closed => title => loc('Closed tickets'), path => '/SelfService/Closed.html' );
881 my $username = '<span class="current-user">'
882 . $m->interp->apply_escapes($session{'CurrentUser'}->Name, 'h')
884 my $about_me = Menu->child( preferences =>
885 title => loc('Logged in as [_1]', $username),
890 if ( $session{'CurrentUser'}->HasRight( Right => 'ModifySelf', Object => RT->System ) ) {
891 $about_me->child( prefs => title => loc('Preferences'), path => '/SelfService/Prefs.html' );
894 if ( $session{'CurrentUser'}->Name
895 && ( !RT->Config->Get('WebExternalAuth')
896 || RT->Config->Get('WebFallbackToInternalAuth') )) {
897 $about_me->child( logout => title => loc('Logout'), path => '/NoAuth/Logout.html' );
900 if ($session{'CurrentUser'}->HasRight( Right => 'ShowArticle', Object => RT->System )) {
901 PageWidgets->child( 'goto-article' => raw_html => $m->scomp('/SelfService/Elements/SearchArticle') );
904 PageWidgets->child( goto => raw_html => $m->scomp('/SelfService/Elements/GotoTicket') );
906 $m->callback( CallbackName => 'SelfService' );
911 if ( $request_path !~ m{^/SelfService/} ) {
914 $build_selfservice_nav->();