From: mark Date: Mon, 27 Jun 2011 07:11:01 +0000 (+0000) Subject: self-service ticket priority and edit subject, #13199 X-Git-Tag: freeside_2_3_0~64 X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=commitdiff_plain;h=12f4cc4b100b849de3584d5d1a2376cebcd8729f self-service ticket priority and edit subject, #13199 --- diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm index fa0bbb8a7..a788a5c05 100644 --- a/FS/FS/ClientAPI/MyAccount.pm +++ b/FS/FS/ClientAPI/MyAccount.pm @@ -74,7 +74,7 @@ sub skin_info { or die "no agentnum for custnum $custnum"; #} elsif ( $context eq 'agent' ) { - } elsif ( $p->{'agentnum'} =~ /^(\d+)$/ ) { + } elsif ( defined($p->{'agentnum'}) and $p->{'agentnum'} =~ /^(\d+)$/ ) { $agentnum = $1; } @@ -97,7 +97,7 @@ sub skin_info { $skin_info_cache_agent = { 'agentnum' => $agentnum, ( map { $_ => scalar( $conf->config($_, $agentnum) ) } - qw( company_name ) ), + qw( company_name date_format ) ), ( map { $_ => scalar( $conf->config("selfservice-$_", $agentnum ) ) } qw( body_bgcolor box_bgcolor text_color link_color vlink_color hlink_color alink_color @@ -295,6 +295,10 @@ sub access_info { $info->{'self_suspend_reason'} = $conf->config('selfservice-self_suspend_reason', $cust_main->agentnum); + $info->{'edit_ticket_subject'} = + $conf->exists('ticket_system-selfservice_edit_subject') && + $cust_main->edit_subject; + return { %$info, 'custnum' => $custnum, 'access_pkgnum' => $session->{'pkgnum'}, @@ -316,7 +320,12 @@ sub customer_info { }else{ $return{'require_address2'} = ''; } - + + if ( $conf->exists('ticket_system') ) { + warn "$me customer_info: initializing ticket system\n" if $DEBUG; + FS::TicketSystem->init(); + } + if ( $custnum ) { #customer record my $search = { 'custnum' => $custnum }; @@ -1909,13 +1918,13 @@ sub create_ticket { ); if ( ref($err_or_ticket) ) { - warn "$me create_ticket: sucessful: ". $err_or_ticket->id. "\n" + warn "$me create_ticket: successful: ". $err_or_ticket->id. "\n" if $DEBUG; return { 'error' => '', 'ticket_id' => $err_or_ticket->id, }; } else { - warn "$me create_ticket: unsucessful: $err_or_ticket\n" + warn "$me create_ticket: unsuccessful: $err_or_ticket\n" if $DEBUG; return { 'error' => $err_or_ticket }; } @@ -2009,62 +2018,135 @@ sub get_ticket { warn "$me get_ticket: initializing ticket system\n" if $DEBUG; FS::TicketSystem->init(); + return { 'error' => 'get_ticket configuration error' } + if $FS::TicketSystem::system ne 'RT_Internal'; + + # check existence and ownership as part of this + warn "$me get_ticket: fetching ticket\n" if $DEBUG; + my $rt_session = FS::TicketSystem->session(''); + my $Ticket = FS::TicketSystem->get_ticket_object( + $rt_session, + ticket_id => $p->{'ticket_id'}, + custnum => $custnum + ); + return { 'error' => 'ticket not found' } if !$Ticket; + + if ( length( $p->{'subject'} || '' ) ) { + # subject change + if ( $p->{'subject'} ne $Ticket->Subject ) { + my ($val, $msg) = $Ticket->SetSubject($p->{'subject'}); + return { 'error' => "unable to set subject: $msg" } if !$val; + } + } if(length($p->{'reply'})) { -# currently this allows anyone to correspond on any ticket as fs_selfservice -# probably bad... - my @err_or_res = FS::TicketSystem->correspond_ticket( - '', #create RT session based on FS CurrentUser (fs_selfservice) - 'ticket_id' => $p->{'ticket_id'}, - 'content' => $p->{'reply'}, - ); - + my @err_or_res = FS::TicketSystem->correspond_ticket( + $rt_session, + 'ticket_id' => $p->{'ticket_id'}, + 'content' => $p->{'reply'}, + ); + return { 'error' => 'unable to reply to ticket' } - unless ( $err_or_res[0] != 0 && defined $err_or_res[2] ); + unless ( $err_or_res[0] != 0 && defined $err_or_res[2] ); } - warn "$me get_ticket: getting ticket\n" if $DEBUG; + warn "$me get_ticket: getting ticket history\n" if $DEBUG; my $err_or_ticket = FS::TicketSystem->get_ticket( - '', #create RT session based on FS CurrentUser (fs_selfservice) + $rt_session, 'ticket_id' => $p->{'ticket_id'}, ); - if ( ref($err_or_ticket) ) { + if ( !ref($err_or_ticket) ) { # there is no way this should ever happen + warn "$me get_ticket: unsuccessful: $err_or_ticket\n" + if $DEBUG; + return { 'error' => $err_or_ticket }; + } -# since we're bypassing the RT security/permissions model by always using -# fs_selfservice as the RT user (as opposed to a requestor, which we -# can't do since we want all tickets linked to a cust), we check below whether -# the requested ticket was actually linked to this customer - my @custs = @{$err_or_ticket->{'custs'}}; - my @txns = @{$err_or_ticket->{'txns'}}; - my @filtered_txns; + my @custs = @{$err_or_ticket->{'custs'}}; + my @txns = @{$err_or_ticket->{'txns'}}; + my @filtered_txns; - return { 'error' => 'no customer' } unless ( $custnum && scalar(@custs) ); + # superseded by check in get_ticket_object + #return { 'error' => 'invalid ticket requested' } + #unless grep($_ eq $custnum, @custs); - return { 'error' => 'invalid ticket requested' } - unless grep($_ eq $custnum, @custs); + foreach my $txn ( @txns ) { + push @filtered_txns, $txn + if ($txn->{'type'} eq 'EmailRecord' + || $txn->{'type'} eq 'Correspond' + || $txn->{'type'} eq 'Create'); + } - foreach my $txn ( @txns ) { - push @filtered_txns, $txn - if ($txn->{'type'} eq 'EmailRecord' - || $txn->{'type'} eq 'Correspond' - || $txn->{'type'} eq 'Create'); + warn "$me get_ticket: successful: \n" + if $DEBUG; + return { 'error' => '', + 'transactions' => \@filtered_txns, + 'ticket_fields' => $err_or_ticket->{'fields'}, + 'ticket_id' => $p->{'ticket_id'}, + }; +} + +sub adjust_ticket_priority { + my $p = shift; + my($context, $session, $custnum) = _custoragent_session_custnum($p); + return { 'error' => $session } if $context eq 'error'; + + warn "$me adjust_ticket_priority: initializing ticket system\n" if $DEBUG; + FS::TicketSystem->init; + my $ss_priority = FS::TicketSystem->selfservice_priority; + + return { 'error' => 'adjust_ticket_priority configuration error' } + if $FS::TicketSystem::system ne 'RT_Internal' + or !$ss_priority; + + my $values = $p->{'values'}; #hashref, id => priority value + my %ticket_error; + + foreach my $id (keys %$values) { + warn "$me adjust_ticket_priority: fetching ticket $id\n" if $DEBUG; + my $Ticket = FS::TicketSystem->get_ticket_object('', + 'ticket_id' => $id, + 'custnum' => $custnum, + ); + if ( !$Ticket ) { + $ticket_error{$id} = 'ticket not found'; + next; + } + + # RT API stuff--would we gain anything by wrapping this in FS::TicketSystem? + # We're not going to implement it for RT_External. + my $old_value = $Ticket->FirstCustomFieldValue($ss_priority); + my $new_value = $values->{$id}; + next if $old_value eq $new_value; + + warn "$me adjust_ticket_priority: updating ticket $id\n" if $DEBUG; + + # AddCustomFieldValue works fine (replacing any existing value) if it's + # a single-valued custom field, which it should be. If it's not, you're + # doing something wrong. + my ($val, $msg); + if ( length($new_value) ) { + ($val, $msg) = $Ticket->AddCustomFieldValue( + Field => $ss_priority, + Value => $new_value, + ); + } + else { + ($val, $msg) = $Ticket->DeleteCustomFieldValue( + Field => $ss_priority, + Value => $old_value, + ); } - warn "$me get_ticket: sucessful: \n" - if $DEBUG; - return { 'error' => '', - 'transactions' => \@filtered_txns, - 'ticket_id' => $p->{'ticket_id'}, - }; - } else { - warn "$me create_ticket: unsucessful: $err_or_ticket\n" - if $DEBUG; - return { 'error' => $err_or_ticket }; + $ticket_error{$id} = $msg if !$val; + warn "$me adjust_ticket_priority: $id: $msg\n" if $DEBUG and !$val; } + return { 'error' => '', + 'ticket_error' => \%ticket_error, + %{ customer_info($p) } # send updated customer info back + } } - #-- sub _custoragent_session_custnum { diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 7d9d6c736..170d88479 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -2540,7 +2540,7 @@ and customer address. Include units.', { 'key' => 'ticket_system', - 'section' => '', + 'section' => 'ticketing', 'description' => 'Ticketing system integration. RT_Internal uses the built-in RT ticketing system (see the integrated ticketing installation instructions). RT_External accesses an external RT installation in a separate database (local or remote).', 'type' => 'select', #'select_enum' => [ '', qw(RT_Internal RT_Libs RT_External) ], @@ -2558,7 +2558,7 @@ and customer address. Include units.', { 'key' => 'ticket_system-default_queueid', - 'section' => '', + 'section' => 'ticketing', 'description' => 'Default queue used when creating new customer tickets.', 'type' => 'select-sub', 'options_sub' => sub { @@ -2584,13 +2584,13 @@ and customer address. Include units.', }, { 'key' => 'ticket_system-force_default_queueid', - 'section' => '', + 'section' => 'ticketing', 'description' => 'Disallow queue selection when creating new tickets from customer view.', 'type' => 'checkbox', }, { 'key' => 'ticket_system-selfservice_queueid', - 'section' => '', + 'section' => 'ticketing', 'description' => 'Queue used when creating new customer tickets from self-service. Defautls to ticket_system-default_queueid if not specified.', #false laziness w/above 'type' => 'select-sub', @@ -2618,49 +2618,63 @@ and customer address. Include units.', { 'key' => 'ticket_system-requestor', - 'section' => '', + 'section' => 'ticketing', 'description' => 'Email address to use as the requestor for new tickets. If blank, the customer\'s invoicing address(es) will be used.', 'type' => 'text', }, { 'key' => 'ticket_system-priority_reverse', - 'section' => '', + 'section' => 'ticketing', 'description' => 'Enable this to consider lower numbered priorities more important. A bad habit we picked up somewhere. You probably want to avoid it and use the default.', 'type' => 'checkbox', }, { 'key' => 'ticket_system-custom_priority_field', - 'section' => '', + 'section' => 'ticketing', 'description' => 'Custom field from the ticketing system to use as a custom priority classification.', 'type' => 'text', }, { 'key' => 'ticket_system-custom_priority_field-values', - 'section' => '', + 'section' => 'ticketing', 'description' => 'Values for the custom field from the ticketing system to break down and sort customer ticket lists.', 'type' => 'textarea', }, { 'key' => 'ticket_system-custom_priority_field_queue', - 'section' => '', + 'section' => 'ticketing', 'description' => 'Ticketing system queue in which the custom field specified in ticket_system-custom_priority_field is located.', 'type' => 'text', }, { + 'key' => 'ticket_system-selfservice_priority_field', + 'section' => 'ticketing', + 'description' => 'Custom field from the ticket system to use as a customer-managed priority field.', + 'type' => 'text', + }, + + { + 'key' => 'ticket_system-selfservice_edit_subject', + 'section' => 'ticketing', + 'description' => 'Allow customers to edit ticket subjects through selfservice.', + 'type' => 'checkbox', + }, + + { 'key' => 'ticket_system-escalation', - 'section' => '', + 'section' => 'ticketing', 'description' => 'Enable priority escalation of tickets as part of daily batch processing.', 'type' => 'checkbox', }, { 'key' => 'ticket_system-rt_external_datasrc', - 'section' => '', + 'section' => 'ticketing', 'description' => 'With external RT integration, the DBI data source for the external RT installation, for example, DBI:Pg:user=rt_user;password=rt_word;host=rt.example.com;dbname=rt', 'type' => 'text', @@ -2668,7 +2682,7 @@ and customer address. Include units.', { 'key' => 'ticket_system-rt_external_url', - 'section' => '', + 'section' => 'ticketing', 'description' => 'With external RT integration, the URL for the external RT installation, for example, https://rt.example.com/rt', 'type' => 'text', }, diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index bf84f0b2b..c786176ec 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -881,6 +881,7 @@ sub tables_hashref { 'email_csv_cdr', 'char', 'NULL', 1, '', '', 'accountcode_cdr', 'char', 'NULL', 1, '', '', 'billday', 'int', 'NULL', '', '', '', + 'edit_subject', 'char', 'NULL', 1, '', '', ], 'primary_key' => 'custnum', 'unique' => [ [ 'agentnum', 'agent_custid' ] ], diff --git a/FS/FS/TicketSystem/RT_External.pm b/FS/FS/TicketSystem/RT_External.pm index 8a8c3ffb4..f976ac0e3 100644 --- a/FS/FS/TicketSystem/RT_External.pm +++ b/FS/FS/TicketSystem/RT_External.pm @@ -403,5 +403,9 @@ sub create_ticket { return 'create_ticket unimplemented w/external RT (write something w/RT::Client::REST?)'; } +sub init { } #unimplemented + +sub selfservice_priority { '' } #unimplemented + 1; diff --git a/FS/FS/TicketSystem/RT_Internal.pm b/FS/FS/TicketSystem/RT_Internal.pm index 6ae8881a4..220b4a011 100644 --- a/FS/FS/TicketSystem/RT_Internal.pm +++ b/FS/FS/TicketSystem/RT_Internal.pm @@ -35,7 +35,6 @@ sub baseurl { sub access_right { my( $self, $session, $right ) = @_; - #return '' unless $conf->config('ticket_system'); return '' unless FS::Conf->new->config('ticket_system'); $session = $self->session($session); @@ -63,41 +62,34 @@ sub session { $session; } +my $firsttime = 1; + sub init { my $self = shift; + if ( $firsttime ) { + + # this part only needs to be done once + warn "$me init: loading RT libraries\n" if $DEBUG; + eval ' + use lib ( "/opt/rt3/local/lib", "/opt/rt3/lib" ); + use RT; + + #for web external auth... + use RT::Interface::Web; + '; + die $@ if $@; + + warn "$me init: loading RT config\n" if $DEBUG; + { + local $SIG{__DIE__}; + eval 'RT::LoadConfig();'; + } + die $@ if $@; - warn "$me init: loading RT libraries\n" if $DEBUG; - eval ' - use lib ( "/opt/rt3/local/lib", "/opt/rt3/lib" ); - use RT; - #it looks like the rest are taken care of these days in RT::InitClasses - #use RT::Ticket; - #use RT::Transactions; - #use RT::Users; - #use RT::CurrentUser; - #use RT::Templates; - #use RT::Queues; - #use RT::ScripActions; - #use RT::ScripConditions; - #use RT::Scrips; - #use RT::Groups; - #use RT::GroupMembers; - #use RT::CustomFields; - #use RT::CustomFieldValues; - #use RT::ObjectCustomFieldValues; - - #for web external auth... - use RT::Interface::Web; - '; - die $@ if $@; - - warn "$me init: loading RT config\n" if $DEBUG; - { - local $SIG{__DIE__}; - eval 'RT::LoadConfig();'; + $firsttime = 0; } - die $@ if $@; + # this needs to be done on each fork warn "$me init: initializing RT\n" if $DEBUG; { local $SIG{__DIE__}; @@ -108,6 +100,106 @@ sub init { warn "$me init: complete" if $DEBUG; } +=item customer_tickets CUSTNUM [ LIMIT ] [ PRIORITYVALUE ] + +Replacement for the one in RT_External so that we can access custom fields +properly. + +=cut + +sub _customer_tickets_search { + my ( $self, $custnum, $limit, $priority ) = @_; + + $custnum =~ /^\d+$/ or die "invalid custnum: $custnum"; + $limit =~ /^\d+$/ or die "invalid limit: $limit"; + + my $session = $self->session(); + my $CurrentUser = $session->{CurrentUser} + or die "unable to create an RT session"; + + my $Tickets = RT::Tickets->new($CurrentUser); + + my $rtql = "MemberOf = 'freeside://freeside/cust_main/$custnum'"; + + if ( defined( $priority ) ) { + my $custom_priority = FS::Conf->new->config('ticket_system-custom_priority_field'); + $rtql .= " AND CF.{$custom_priority} = '$priority'"; + } + + $rtql .= ' AND ( ' . + join(' OR ', map { "Status = '$_'" } $self->statuses) . + ' )'; + + $Tickets->FromSQL($rtql); + + $Tickets->RowsPerPage($limit); + + return $Tickets; +} + +sub customer_tickets { + my $Tickets = _customer_tickets_search(@_); + + my $conf = FS::Conf->new; + my $priority_order = + $conf->exists('ticket_system-priority_reverse') ? 'ASC' : 'DESC'; + my $custom_priority = + $conf->config('ticket_system-custom_priority_field') || ''; + + my @order_by; + my $ss_priority = selfservice_priority(); + push @order_by, { FIELD => "CF.{$ss_priority}", ORDER => $priority_order } + if $ss_priority; + push @order_by, + { FIELD => 'Priority', ORDER => $priority_order }, + { FIELD => 'Id', ORDER => 'DESC' }, + ; + + $Tickets->OrderByCols(@order_by); + + my @tickets; + while ( my $t = $Tickets->Next ) { + push @tickets, _ticket_info($t); + } + return \@tickets; +} + +sub num_customer_tickets { + my $Tickets = _customer_tickets_search(@_); + return $Tickets->CountAll; +} + +sub _ticket_info { + # Takes an RT::Ticket; returns a hashref of the ticket's fields, including + # custom fields. Also returns custom and selfservice priority values as + # _custom_priority and _selfservice_priority. + my $t = shift; + + my $custom_priority = + FS::Conf->new->config('ticket_system-custom_priority_field') || ''; + my $ss_priority = selfservice_priority(); + + my %ticket_info; + foreach my $name ( $t->ReadableAttributes ) { + # lowercase names, and skip attributes with non-scalar values + $ticket_info{lc($name)} = $t->$name if !ref($t->$name); + } + $ticket_info{'owner'} = $t->OwnerObj->Name; + $ticket_info{'queue'} = $t->QueueObj->Name; + foreach my $CF ( @{ $t->CustomFields->ItemsArrayRef } ) { + my $name = 'CF.{'.$CF->Name.'}'; + $ticket_info{$name} = $t->CustomFieldValuesAsString($CF->Id); + } + # make this easy to find + if ( $custom_priority ) { + $ticket_info{'_custom_priority'} = $ticket_info{"CF.{$custom_priority}"}; + } + if ( $ss_priority ) { + $ticket_info{'_selfservice_priority'} = $ticket_info{"CF.{$ss_priority}"}; + } + return \%ticket_info; +} + =item create_ticket SESSION_HASHREF, OPTION => VALUE ... Class method. Creates a ticket. If there is an error, returns the scalar @@ -219,8 +311,8 @@ sub create_ticket { Class method. Retrieves a ticket. If there is an error, returns the scalar error. Otherwise, currently returns a slightly tricky data structure containing -a list of the linked customers and each transaction's content, description, and -create time. +the ticket's attributes, a list of the linked customers, each transaction's +content, description, and create time. Accepts the following options: @@ -262,9 +354,50 @@ sub get_ticket { { txns => [ @txns ], custs => [ @custs ], + fields => _ticket_info($Ticket), }; } +=item get_ticket_object SESSION_HASHREF, OPTION => VALUE... + +Class method. Retrieve the RT::Ticket object with the specified +ticket_id. If custnum is supplied, will also check that the object +is a member of that customer. If there is no ticket or the custnum +check fails, returns nothing. The meaning of that case is +"to this customer, the ticket does not exist". + +Options: + +=over 4 + +=item ticket_id + +=item custnum + +=back + +=cut + +sub get_ticket_object { + my $self = shift; + my ($session, %opt) = @_; + $session = $self->session(shift); + my $Ticket = RT::Ticket->new($session->{CurrentUser}); + $Ticket->Load($opt{'ticket_id'}); + return if ( !$Ticket->id ); + my $custnum = $opt{'custnum'}; + if ( defined($custnum) && $custnum =~ /^\d+$/ ) { + # probably the most efficient way to check ticket ownership + my $Link = RT::Link->new($session->{CurrentUser}); + $Link->LoadByCols( LocalBase => $opt{'ticket_id'}, + Type => 'MemberOf', + Target => "freeside://freeside/cust_main/$custnum", + ); + return if ( !$Link->id ); + } + return $Ticket; +} + =item correspond_ticket SESSION_HASHREF, OPTION => VALUE ... @@ -427,5 +560,20 @@ sub _web_external_auth { } +=item selfservice_priority + +Returns the configured self-service priority field. + +=cut + +my $selfservice_priority; + +sub selfservice_priority { + return $selfservice_priority ||= do { + my $conf = FS::Conf->new; + $conf->config('ticket_system-selfservice_priority_field') || ''; + } +} + 1; diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 4a7ad2ead..b1f71fd3f 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -324,6 +324,10 @@ A suggestion to events (see L) to delay until this unix ti Discourage individual CDR printing, empty or `Y' +=item edit_subject + +Allow self-service editing of ticket subjects, empty or 'Y' + =back =head1 METHODS diff --git a/fs_selfservice/FS-SelfService/SelfService.pm b/fs_selfservice/FS-SelfService/SelfService.pm index ec0329bd2..0a153be45 100644 --- a/fs_selfservice/FS-SelfService/SelfService.pm +++ b/fs_selfservice/FS-SelfService/SelfService.pm @@ -64,6 +64,7 @@ $socket .= '.'.$tag if defined $tag && length($tag); 'myaccount_passwd' => 'MyAccount/myaccount_passwd', 'create_ticket' => 'MyAccount/create_ticket', 'get_ticket' => 'MyAccount/get_ticket', + 'adjust_ticket_priority' => 'MyAccount/adjust_ticket_priority', 'did_report' => 'MyAccount/did_report', 'signup_info' => 'Signup/signup_info', 'skin_info' => 'MyAccount/skin_info', diff --git a/fs_selfservice/FS-SelfService/cgi/selfservice.cgi b/fs_selfservice/FS-SelfService/cgi/selfservice.cgi index 0e8b990da..58f2d0f1b 100644 --- a/fs_selfservice/FS-SelfService/cgi/selfservice.cgi +++ b/fs_selfservice/FS-SelfService/cgi/selfservice.cgi @@ -18,6 +18,7 @@ use FS::SelfService qw( unprovision_svc change_pkg suspend_pkg domainselector list_svcs list_svc_usage list_cdr_usage list_support_usage myaccount_passwd list_invoices create_ticket get_ticket did_report + adjust_ticket_priority mason_comp port_graph ); @@ -77,6 +78,7 @@ my @actions = ( qw( myaccount tktcreate tktview + ticket_priority didreport invoices view_invoice @@ -276,11 +278,28 @@ sub tktcreate { sub tktview { get_ticket( 'session_id' => $session_id, - 'ticket_id' => $cgi->param('ticket_id'), - 'reply' => $cgi->param('reply'), + 'ticket_id' => ($cgi->param('ticket_id') || ''), + 'subject' => ($cgi->param('subject') || ''), + 'reply' => ($cgi->param('reply') || ''), ); } +sub ticket_priority { + my %values; + foreach ( $cgi->param ) { + if ( /^ticket(\d+)$/ ) { + # a 'ticket1001' param implies the existence of a 'priority1001' param + # but if that's empty, we need to send it as empty rather than forget + # it. + $values{$1} = $cgi->param("priority$1") || ''; + } + } + $action = 'myaccount'; + # this returns an updated customer_info for myaccount + adjust_ticket_priority( 'session_id' => $session_id, + 'values' => \%values ); +} + sub customer_order_pkg { my $init_data = signup_info( 'customer_session_id' => $session_id ); return $init_data if ( $init_data->{'error'} ); diff --git a/fs_selfservice/FS-SelfService/cgi/ticket_summary.html b/fs_selfservice/FS-SelfService/cgi/ticket_summary.html new file mode 100644 index 000000000..0d1c5e99a --- /dev/null +++ b/fs_selfservice/FS-SelfService/cgi/ticket_summary.html @@ -0,0 +1,65 @@ + + + + +<%= +my $col1 = "ffffff"; +my $col2 = "dddddd"; +my $col = $col1; + +my $can_set_priority = + grep { exists($_->{'_selfservice_priority'}) } @tickets; +if ( $can_set_priority ) { + $OUT .= qq!! . + qq!! . + qq!!; +} +$date_format ||= '%Y-%m-%d'; +my $date_formatter = sub { + my $time = Date::Parse::str2time($_[0], 'GMT'); # RT internal dates are GMT + # exclude times within 24 hours of zero + ($time > 86400) ? Date::Format::time2str($date_format, $time) : '' +}; + +my @titles = ('#', qw(Subject Queue Status Created Due)); +push @titles, 'Estimated
Minutes'; +push @titles, 'Priority'; + +$OUT .= join("\n", map { "" } @titles) . ''; + +foreach my $ticket ( @tickets ) { + my $id = $ticket->{'id'}; + my $td = qq!' . $td . $link . $id . ''. +$td. $link . $ticket->{'subject'} . ''. +$td. $ticket->{'queue'} . ''. +$td. $ticket->{'status'} . ''. +$td. $date_formatter->($ticket->{'created'}) . ''. +$td. $date_formatter->($ticket->{'due'}) . ''. +qq!'. +qq!'; + if ( exists($ticket_error{$id}) ) { + # display error message compactly + $OUT .= ''; + } + } + else { + $OUT .= ($ticket->{'content'} || $ticket->{'priority'}) . ''; + } + $OUT .= ''; + $col = $col eq $col1 ? $col2 : $col1; +} #foreach my $ticket +if ( $can_set_priority ) { + $OUT .= ''; +} +%> +
Open Tickets
$_
!; + my $link = qq!!; + $OUT .= '
!. ($ticket->{'timeestimated'} || '') +. '!; + if ( $can_set_priority ) { + $OUT .= '' . + '*
+
diff --git a/fs_selfservice/FS-SelfService/cgi/tktview.html b/fs_selfservice/FS-SelfService/cgi/tktview.html index 6f540bcf4..72634fee2 100644 --- a/fs_selfservice/FS-SelfService/cgi/tktview.html +++ b/fs_selfservice/FS-SelfService/cgi/tktview.html @@ -22,9 +22,16 @@ else {
+<%= if ( $edit_ticket_subject ) { $OUT .= ' + Subject:
+

'; +} +%> Add reply to ticket: -
+
+
diff --git a/httemplate/config/config-view.cgi b/httemplate/config/config-view.cgi index 10fcde3c4..4c90ebb8d 100644 --- a/httemplate/config/config-view.cgi +++ b/httemplate/config/config-view.cgi @@ -350,7 +350,7 @@ my @config_items = grep { $page_agent ? $_->per_agent : 1 } my @deleteable = qw( invoice_latexreturnaddress invoice_htmlreturnaddress ); my %deleteable = map { $_ => 1 } @deleteable; -my @sections = qw(required billing invoicing notification UI self-service username password session shell BIND telephony ); +my @sections = qw(required billing invoicing notification UI self-service ticketing username password session shell BIND telephony ); push @sections, '', 'deprecated'; my %section_items = (); diff --git a/httemplate/edit/cust_main/top_misc.html b/httemplate/edit/cust_main/top_misc.html index 575b73727..a7545a045 100644 --- a/httemplate/edit/cust_main/top_misc.html +++ b/httemplate/edit/cust_main/top_misc.html @@ -108,6 +108,16 @@ &> % } +% # permission to edit ticket subjects +% if ( $conf->exists('ticket_system-selfservice_edit_subject') ) { + + + edit_subject ? 'CHECKED' : '' %>> + <% mt('Can edit ticket subjects') |h %> + +% } + <%init> diff --git a/httemplate/view/cust_main/tickets.html b/httemplate/view/cust_main/tickets.html index 2175110e4..0c48d2196 100644 --- a/httemplate/view/cust_main/tickets.html +++ b/httemplate/view/cust_main/tickets.html @@ -12,8 +12,7 @@ function updateTicketLink() { <% mt('Create new ticket') |h %> <% mt('in queue') |h %> %# fetch list of queues in which the user can create tickets -% my $session = FS::TicketSystem->session(); -% my %queues = FS::TicketSystem->queues($session, 'CreateTicket'); +% my %queues = FS::TicketSystem->queues('', 'CreateTicket'); % if( $conf->exists('ticket_system-force_default_queueid') ) { <% $queues{$new_param{'Queue'}} %> @@ -46,7 +45,12 @@ function updateTicketLink() { <% mt('Status') |h %> <% mt('Queue') |h %> <% mt('Owner') |h %> + <% mt('Due') |h %> + <% mt('Estimated Time') |h %> <% mt('Priority') |h %> +% if ( $ss_priority ) { + <% mt('Customer Priority') |h %> +% } % foreach my $ticket ( @tickets ) { @@ -78,6 +82,14 @@ function updateTicketLink() { <% $ticket->{owner} %> + + + <% $date_formatter->($ticket->{due}) %> + + + + <% $ticket->{timeestimated} %> + <% $ticket->{content} @@ -85,7 +97,13 @@ function updateTicketLink() { : $ticket->{priority} %> - + +% if ( $ss_priority ) { + + <% $ticket->{"CF.{$ss_priority}"} %> + +% } + % } @@ -93,6 +111,8 @@ function updateTicketLink() { <%init> +use Date::Format 'time2str'; +use Date::Parse 'str2time'; my( $conf ) = new FS::Conf; my( $cust_main ) = @_; @@ -102,7 +122,7 @@ my $open_link = FS::TicketSystem->href_customer_tickets($cust_main->custnum); my $openlabel = join('/', FS::TicketSystem->statuses ); my $res_link = FS::TicketSystem->href_customer_tickets( - $cust_main->custnum, + $cust_main->Custnum, { 'statuses' => [ 'resolved' ] } ); @@ -111,4 +131,14 @@ my( $new_base, %new_param ) = my $new_link = FS::TicketSystem->href_new_ticket( $cust_main ); +my $ss_priority = FS::TicketSystem->selfservice_priority; + +my $format = $conf->config('date_format') || '%Y-%m-%d'; + +my $date_formatter = sub { + my $time = str2time($_[0], 'GMT'); + # exclude times within 24 hours of zero + ($time > 86400) ? time2str($format, $time) : ''; +}; +