package TTXTicket; # # This module is a part of Trouble Ticket Express package # http://www.troubleticketexpress.com # # COPYRIGHT: 2002-2006, United Web Coders # http://www.unitedwebcoders.com # # $Revision: 287 $ # $Date: 2006-10-17 16:00:49 +0400 (Tue, 17 Oct 2006) $ # $TTXTicket::VERSION='2.22'; BEGIN { $TTXTicket::REVISION = '$Revision: 287 $'; if ($TTXTicket::REVISION =~ /(\d+)/) { $TTXTicket::REVISION = $1; } }; use strict; use TTXCommon; require TTXUser; use TTXTickets; require TTXMail; require TTXMarkup; require TTXDictionary; my $useGD; # =================================================================== invenabled my $_invenabled = undef; sub invenabled { if ($_invenabled eq undef) { eval 'use TTXInvMod'; if ($@ eq undef) { $_invenabled = 1; } else { $_invenabled = 0; } } return $_invenabled; } # ====================================================================== company sub company { my ($cfg, $ticket) = @_; my $cid = $cfg->get('customcompany'); my $title; if ($cid ne undef && $cid =~ /(\d+)/) { $cid = $1; $title = $ticket->{"c$cid"}; } if ($title eq undef) { $title = $cfg->get('company'); } return $title; } # =================================================================== usernotify sub usernotify { my $cfg = $_[0]->{cfg}; my $ticket = $_[0]->{ticket}; my $email = $ticket->{'email'}; my $name = $ticket->{'name'}; my %macros; $name = '[%Customer%]' if $name eq undef; my $subject; my $messages = $ticket->{messages}; my $tmpl; if (@{$messages} == 1) { $tmpl = 'newticket'; if ($cfg->get('validate') ne undef) { $tmpl .= 'validate'; $macros{ENV_HTTP_HOST} = $ENV{HTTP_HOST}; $macros{VALIDATIONCODE} = $_[0]->{validationcode}; } else { return if $cfg->get('_USER') eq undef && $cfg->get('notikcfrm'); } } else { return if $cfg->get('_USER') eq undef && $cfg->get('nomsgcfrm'); $tmpl = 'newmessage'; } if (open(TMPL, $cfg->get('basedir')."/templates/$tmpl.txt")) { my @buff = ; close TMPL; $tmpl = join('', @buff); } $macros{TICKETID} = $ticket->{id}; $macros{TICKETSTATE} = TTXDictionary::translate(TTXCommon::status($ticket->{status})); $macros{UNAME} = $name; $macros{COMPANYNAME} = company($cfg, $ticket); $macros{UEMAIL} = $email; $macros{OPERATORNAME} = '-'; $macros{FROM} = $_[0]->{from}; $macros{GROUP} = ($ticket->{grp} ne undef) ? $cfg->get($ticket->{grp}) : '-'; my $oper; if ($cfg->get('_USER') ne undef) { $oper = $cfg->get('_USER'); } elsif ($ticket->{oper} ne undef) { $oper = TTXUser->new($ticket->{oper}); } if ($oper ne undef) { $macros{OPERATORNAME} = $oper->{fname}.' '.$oper->{lname}; } my $respondername; if ($cfg->get('askforname') ne undef) { my $nm = $name; $nm =~ s/[="'&><]//g; $nm =~ s/\s/+/g; $respondername = "&respondername=$nm"; } $macros{TICKETURL} = '[url'." http://$ENV{HTTP_HOST}$ENV{SCRIPT_NAME}?cmd=ticket&key=".$ticket->{key}.$respondername. '][b]Ticket '.$macros{TICKETID}.'[/b][/url]'."\n\nACCESS KEY: ".$ticket->{key}; my $msg = $messages->[@{$messages} - 1]; my @msgparts = split(/\n\n/, $msg); shift @msgparts; my $body = join("\n\n", @msgparts); # $body =~s/\[b]//g; # $body =~s/:\[\/b]/:/g; $macros{MESSAGE} = $body; my $cnt = TTXCommon::cfldcnt(); for (my $i = 0; $i < $cnt; ++$i) { $macros{"C$i"} = $ticket->{"c$i"}; } my $tmplcc = $tmpl; $tmpl =~ s/\(%\s*([A-Z0-9_]+)\s*%\)/$macros{$1}/g; $tmpl =~ s/\[%%\]//g; $tmpl =~ s/\[%([^]]+)%\]/TTXDictionary::translate($1)/ge; $subject = TTXDictionary::translate('Ticket').' '.$ticket->{id}; $subject .= ", ".$ticket->{subject} if $ticket->{subject} ne undef; TTXMail::sendmail( { to => $email, from => "\"".company($cfg, $ticket)."\" <".$cfg->get('email').">", # from => "\"".$cfg->get('company')."\" <" . (($cfg->get('_USER') ne undef && $cfg->get('_USER')->{email} ne undef)? $cfg->get('_USER')->{email}:$cfg->get('email')) . ">", subject => $subject, msg => $tmpl } ); if ($cfg->get('emaillist') ne undef) { my $tmplname; if (@{$messages} == 1) { $tmplname = 'newticket'; $tmplname = 'newccticket' if -f $cfg->get('basedir')."/templates/newccticket.txt"; } else { $tmplname = 'newmessage'; $tmplname = 'newccmessage' if -f $cfg->get('basedir')."/templates/newccmessage.txt";; } if (open(TMPL, $cfg->get('basedir')."/templates/$tmplname.txt")) { my @buff = ; close TMPL; $tmplcc = join('', @buff); } $macros{TICKETURL} = '[url '."http://$ENV{HTTP_HOST}$ENV{SCRIPT_NAME}?cmd=ticket&key=".$ticket->{key}. '][b]Ticket '.$macros{TICKETID}.'[/b][/url]'."\n\nACCESS KEY: ".$ticket->{key}; my $n = $cfg->get('emaillist'); if ($n =~ /^\d\d?$/) { my @addrlist = split(/,/, $ticket->{"c$n"}); $macros{UNAME} = '[%Customer%]'; foreach my $addr (@addrlist) { $addr =~ s/\s//g; $macros{UEMAIL} = $addr; my $tmpltmp = $tmplcc; $tmpltmp =~ s/\(%\s*([A-Z0-9_]+)\s*%\)/$macros{$1}/g; $tmpltmp =~ s/\[%%\]//g; $tmpltmp =~ s/\[%([^]]+)%\]/TTXDictionary::translate($1)/ge; TTXMail::sendmail( { to => $addr, from => "\"".company($cfg, $ticket)."\" <".$cfg->get('email').">", subject => $subject, msg => $tmpltmp } ); } } } } # ================================================================= buildadrlist sub buildadrlist { my @adrlist; foreach my $oper (@{$_[0]}) { my $u = TTXUser->new($oper); if ($u ne undef && $u->{email} ne undef) { push @adrlist, $u->{email}; } } return @adrlist; } # =============================================================== transfernotice sub transfernotice { my $cfg = $_[0]->{cfg}; my $ticket = $_[0]->{ticket}; my $o = $cfg->get('_USER'); return if $o eq undef; my $u = TTXUser->new($ticket->{oper}); return if $u eq undef || $u->{email} eq undef; my $quicklink; if ($cfg->get('quicklink')) { $quicklink = '&o='.$ticket->{oper}; my $salt = $ticket->{id} % 100; $salt = "0$salt" if $salt < 10; my $p = crypt($u->{passwd}.$ticket->{id}, $salt); $p =~ s/^\d\d//; $quicklink .= "&p=$p"; } TTXMail::sendmail( { to => $u->{email}, from => "\"".company($cfg, $ticket)."\" <".$cfg->get('email').">", subject => TTXDictionary::translate('Ticket transfer notice'), msg => '[b]'.TTXDictionary::translate('Ticket ID').':[/b] '.$ticket->{id}."\n". '[b]'.TTXDictionary::translate('Subject').':[/b] '.$ticket->{subject}."\n". '[b]'.TTXDictionary::translate('Transferred by').':[/b] '.$o->{fname}.' '.$o->{lname}.' ('.$o->{login}.") \n". '[b]'.TTXDictionary::translate('Access link').':[/b] '. '[url http://'.$ENV{HTTP_HOST}.$ENV{SCRIPT_NAME}.'?cmd=helpdesk&tk='.$ticket->{key}.$quicklink.']'. TTXDictionary::translate('Ticket').' '.$ticket->{id}.'[/url]'."\n\n" # msg => "Hello ".$u->{fname}.",\n\n".$o->{fname}." ".$o->{lname}." (".$o->{login}.") has transferred ". # "trouble ticket #".$ticket->{id}." to you. In order to access the ticket use this link:\n\n". # "http://$ENV{HTTP_HOST}$ENV{SCRIPT_NAME}?cmd=helpdesk&tk=".$ticket->{key}."$quicklink\n\n" } ); } # =================================================================== opernotify sub opernotify { my $cfg = $_[0]->{cfg}; return if $cfg->get('silent'); my $ticket = $_[0]->{ticket}; my @adrlist; if ($cfg->get('usegrpsel') && $ticket->{status} eq 'PND' && ! exists $_[0]->{notifylist}) { my $grp; if ($ticket->{grp} eq undef) { $grp = $cfg->get('defaultgrp'); } else { $grp = $ticket->{grp}; } my @list = split(/;/, $cfg->get("mbr-$grp")); $_[0]->{notifylist} = \@list; } if (exists $_[0]->{notifylist}) { @adrlist = buildadrlist($_[0]->{notifylist}); } elsif ($ticket->{oper} ne undef) { my @operlist = split(/,/, $ticket->{oper}); push @operlist, $cfg->get('supervisor') if $cfg->get('supervisor') ne undef; @adrlist = buildadrlist(\@operlist); } else { @adrlist = TTXUser::listemail(); } my $email = $ticket->{'email'}; my $name = $ticket->{'name'}; my %macros; $name = '[%Customer%]' if $name eq undef; my $messages = $ticket->{messages}; my $tmpl; if (@{$messages} == 1) { $tmpl = 'onewticket'; } else { $tmpl = 'onewmessage'; } if (open(TMPL, $cfg->get('basedir')."/templates/$tmpl.txt")) { my @buff = ; close TMPL; $tmpl = join('', @buff); } $macros{TICKETID} = $ticket->{id}; if (invenabled()) { my $items = TTXInvMod::getitems($ticket->{item}); my $val = undef; foreach my $item (@{$items}) { $val .= ', ' if $val ne undef; $val .= $item->{title}; } $val = 'none' if $val eq undef; $macros{ITEMLBL} = $cfg->get('inventory.label') || 'Item'; $macros{ITEM} = $val; $macros{ITEMLINE} = "\n[b]$macros{ITEMLBL}:[/b] $macros{ITEM}"; } $macros{GROUP} = ($ticket->{grp} ne undef) ? $cfg->get($ticket->{grp}) : '-'; $macros{TICKETSTATE} = TTXDictionary::translate(TTXCommon::status($ticket->{status})); $macros{UNAME} = $name; $macros{COMPANYNAME} = company($cfg, $ticket); $macros{UEMAIL} = $email; $macros{TICKETURL} = '[url '."http://$ENV{HTTP_HOST}".$cfg->get('scriptname')."?cmd=helpdesk&tk=".$ticket->{key}. '][b]Ticket '.$macros{TICKETID}.'[/b][/url]'; my $msg = $messages->[@{$messages} - 1]; my @msgparts = split(/\n\n/, $msg); shift @msgparts; my $body = join("\n\n", @msgparts); # $body =~s/\[b]//g; # $body =~s/:\[\/b]/:/g; $macros{MESSAGE} = $body; my $cnt = TTXCommon::cfldcnt(); for (my $i = 0; $i < $cnt; ++$i) { $macros{"C$i"} = $ticket->{"c$i"}; } my $tmplcpy; if (!$cfg->get('quicklink')) { $tmpl =~ s/\(%\s*([A-Z0-9_]+)\s*%\)/$macros{$1}/g; $tmpl =~ s/\[%%\]//g; $tmpl =~ s/\[%([^]]+)%\]/TTXDictionary::translate($1)/ge; } else { $tmplcpy = $tmpl; } my @oplist; # got to think about this... if (exists $_[0]->{notifylist}) { @oplist = @{$_[0]->{notifylist}}; } elsif ($ticket->{oper} ne undef) { @oplist = split(/,/, $ticket->{oper}); push @oplist, $cfg->get('supervisor') if $cfg->get('supervisor') ne undef; } else { @oplist = TTXUser::list(); } foreach my $adr (@adrlist) { my $oper = shift @oplist; if ($oper ne undef) { # just to make sure my $u = TTXUser->new($oper); if ($u ne undef && $u->{email} ne undef) { if ($cfg->get('quicklink')) { my $tmp = $macros{TICKETURL}; $macros{TICKETURL} .= '&o='.$oper; my $salt = $ticket->{id} % 100; $salt = "0$salt" if $salt < 10; my $p = crypt($u->{passwd}.$ticket->{id}, $salt); $p =~ s/^\d\d//; $macros{TICKETURL} .= "&p=$p"; $tmpl = $tmplcpy; $tmpl =~ s/\(%\s*([A-Z0-9_]+)\s*%\)/$macros{$1}/g; $tmpl =~ s/\[%%\]//g; $tmpl =~ s/\[%([^]]+)%\]/TTXDictionary::translate($1)/ge; $macros{TICKETURL} = $tmp; } my $subject = TTXDictionary::translate('Ticket').' '.$ticket->{id}; if ($u->{usemail}) { $ticket->{key} =~ /Z(\w\w\w\w)/; $subject .= ' ('.$u->{snum}."-$1)"; } $subject .= ", ".$ticket->{subject} if $ticket->{subject} ne undef; TTXMail::sendmail( { to => $adr, from => "\"".company($cfg, $ticket)."\" <".$cfg->get('email').">", subject => $subject, msg => $tmpl } ); } else { next; } } else { last; } } } # ====================================================================== keyform sub keyform { my ($cfg, $query, $data) = @_; $data->{PAGEHEADING} = '[%Access Key%]'; $data->{MESSAGE} = '[%Forgot the key? We will send it to your email address.%]'; return undef if !$query->param('do'); $query->param(-name => 'do', -value => ''); if ($query->param('cancel') ne undef) { if ($cfg->get('_USER') ne undef) { return 'helpdesk'; } else { return 'newticket'; } } if ($query->param('cmd') eq 'keyform') { my $key = TTXCommon::cleanit($query, 'key'); if ($key eq undef) { $data->{ERROR_MESSAGE} = '[%Missing access key%]'; return undef; } if ($cfg->get('shortkey')) { if ($key !~ /^\d+$/) { $data->{ERROR_MESSAGE} = '[%Invalid access key%]'; return undef; } } elsif ($key !~ /^\d+Z\d+$/) { $data->{ERROR_MESSAGE} = '[%Invalid access key%]'; return undef; } return 'ticket'; } # keyfinder code my $email = TTXCommon::cleanit($query, 'email'); if ($email eq undef) { $data->{ERROR_MESSAGE} = '[%Missing email address%]'; return undef; } if ($email !~ /^[0-9A-Za-z._-]+@[0-9A-Za-z_-]+\.[0-9A-Za-z._-]+$/) { $data->{ERROR_MESSAGE} = '[%Invalid email address%]'; return undef; } my $tickets = TTXCommon::dbtik(); if ($tickets eq undef || $tickets->error() ne undef) { $data->{ERROR_MESSAGE} = $tickets->errortext(); return undef; } my @filter; my $f; $f->{col} = 'email'; $f->{expr} = "^$email\$"; push @filter, $f; my $browser = $tickets->list(0, 999, 'id', 'A', \@filter); my $list = $browser->{list}; if (@{$list} < 1) { $data->{ERROR_MESSAGE} = '[%No tickets found%]'; } else { my $body; foreach my $id (@{$list}) { my $t = $tickets->ticket($id); $body .= "#".$id." (".$t->{subject}.") KEY: ".$t->{key}."\n"; } $tickets = undef; $body = TTXDictionary::translate('Dear customer,')."\n\n". TTXDictionary::translate('This is a list of all service tickets associated '. 'with your email address')."\n\n$body\n"; $body .= TTXDictionary::translate('Use the following link to access your tickets')."\n". "[url http://$ENV{HTTP_HOST}$ENV{SCRIPT_NAME}?cmd=ticket]Show Ticket[/url]\n\n". TTXDictionary::translate('Kind Regards')."\n".$cfg->get('company')."\n"; TTXMail::sendmail( { to => $email, from => "\"".$cfg->get('company')."\" <".$cfg->get('email').">", subject => TTXDictionary::translate('Access key(s)'), msg => $body } ); $data->{MESSAGE} = '[%Access keys were sent to your email address.%]'; } return undef; } # ================================================================== checkgroups my $groupsenabled; sub checkgroups { if ($groupsenabled eq undef) { eval "use TTXGroups"; if (!$@) { $groupsenabled = 1; } else { $groupsenabled = 0; if ($_[0]->get('usegrpsel')) { $_[0]->set('usegrpsel', 0); $_[0]->save(); } } } } # ========================================================================== kid sub kid { my ($cfg, $query, $data, $useold) = @_; eval "use GD" if !$useGD; if ($useGD || $@ eq undef) { $useGD = 1; my $datadir = $cfg->get('basedir'); my $keyid; my $oldid = TTXCommon::cleanit($query, 'kid'); if ($oldid ne undef && $oldid !~ /^\d+-\d+$/) { $oldid = undef; $query->param(-name => 'kid', -value => ''); } if ($oldid ne undef && $useold && (-f "$datadir/keys/$oldid.cgi")) { $keyid = $oldid; } else { $query->param(-name => 'kid', -value => ''); if (-f "$datadir/keys/$oldid.cgi") { unlink "$datadir/keys/$oldid.cgi"; } $keyid = time(). "-$$"; $useold = 0; } if (! -d "$datadir/keys") { mkdir("$datadir/keys", 0777); } if (-d "$datadir/keys") { if (!$useold && open(KF, ">$datadir/keys/$keyid.cgi")) { my $key = 999 + int rand(9000); print KF "$key\n"; close KF; } $data->{KID} = $keyid; my $keycode; if ($useold && $oldid ne undef) { $keycode = TTXCommon::cleanit($query, 'keycode'); $keycode =~ s/"/"/g; } $data->{KIDVAL} = < EOT $data->{KIDIMG} = ""; $data->{KIDFORM} = < [%Access code%]
[%Copy the code here%]:
EOT } } } # ==================================================================== newticket sub newticket { my ($cfg, $query, $data) = @_; my $cfn; # captcha code file name $data->{PAGEHEADING} = '[%Contact Customer Service%]'; if (TTXData::get('ISPRO')) { $data->{FILEFORMS} = TTXFile::fileforms(); } if ($cfg->get('_USER') ne undef) { $data->{INTERNALCHECKBOX} = 'param('internal') ? ' checked':''). '> [%Internal%]'; } else { $data->{INTERNALCHECKBOX} = ' '; } checkgroups($cfg); if ($cfg->get('usegrpsel')) { TTXGroups::selector($cfg, $query, $data); } if ($cfg->get('inventory.show') && invenabled()) { TTXInvMod::selector($cfg, $query, $data); } if ($cfg->get('useopersel')) { eval "use TTXOpers"; if (!$@) { TTXOpers::selector($cfg, $query, $data); } else { $cfg->set('useopersel', 0); } } eval "use TTXLayout"; if (!$@) { my $ldver; eval '$ldver = $TTXLayout::VERSION'; if ($ldver >= '2.22') { my $cfldcnt = TTXCommon::cfldcnt(); for (my $i = 0; $i < $cfldcnt; ++$i) { $data->{"CUSTOM$i"} = TTXLayout::cfld($query, $i); $data->{"CUSTOMROW$i"} = TTXLayout::cfldrow($query, $i); $data->{"CUSTOMVARS"} .= $data->{"CUSTOMROW$i"}; } } } if (!$query->param('do')) { foreach my $input ('email', 'name') { if ($query->param($input) eq undef) { $query->param(-name => $input, -value => $cfg->get("_GLOBAL_newticket_$input")); } } kid($cfg, $query, $data, 0) if $cfg->get('useaccesscode'); return undef; } $query->param(-name => 'do', -value => ''); if ($query->param('cancel') ne undef) { $query->param(-name => 'cancel', -value => ''); if ($cfg->get('_USER') ne undef) { $query->param(-name => 'tid', -value=> ''); return 'helpdesk'; } else { return undef; } } my $ticket; $ticket->{grp} = TTXCommon::cleanit($query, 'grp'); $ticket->{name} = TTXCommon::cleanit($query, 'name'); $ticket->{email} = TTXCommon::cleanit($query, 'email'); $ticket->{subject} = TTXCommon::cleanit($query, 'subject'); if (invenabled()) { my @items = $query->param('item'); if (grep(/^-$/, @items) || !int(@items)) { @items = ('-'); $ticket->{item} = ''; } else { $ticket->{item} = ';'.join(';', @items).';'; } } my $error; if ($ticket->{name} eq undef) { $error .= '[%Missing Name%]
'; } $ticket->{lname} = $ticket->{name}; $ticket->{name} =~ /\s(\w+)$/; $ticket->{lname} = $1 if $1 ne undef; if ($ticket->{email} eq undef) { $error .= '[%Missing Email%]
'."\n"; } elsif ($ticket->{email} !~ /^[0-9A-Za-z'._-]+@[0-9A-Za-z_-]+\.[0-9A-Za-z._-]+$/) { $error .= "Invalid Email
"; } if ($ticket->{grp} eq undef && $cfg->get('usegrpsel')) { $error .= TTXDictionary::translate('Missing').' '.($cfg->get('grpsellbl') || '[%Department%]')."
"; } if ($ticket->{subject} eq undef) { $error .= '[%Missing Subject%]
'; } my $useold = 0; if ($cfg->get('useaccesscode')) { eval "use GD" if !$useGD; if ($useGD || $@ eq undef) { $useGD = 1; my $kid = TTXCommon::cleanit($query, 'kid'); if ($kid !~ /^\d+-\d+$/) { $error .= '[%Corrupted access code%]
'; } else { $cfn = $cfg->get('basedir')."/keys/$kid.cgi"; my $key = TTXCommon::cleanit($query, 'keycode'); if ($key eq undef) { $error .= '[%Missing access code%]
'; } else { if (!open(KF, $cfn)) { $error .= '[%Expired access code%]
'; } else { my $k = ; close KF; chomp $k; if ($k ne $key) { $error .= '[%Invalid access code%]
'; } else { $useold = 1; } } } } } } if ($cfg->get('useopersel') && $query->param('oper') eq undef) { $error .= '[%Missing Operator%] ([%to assign the ticket to%])
'; } my $custom; my @inputs = sort $query->param(); my @mandatory = split(/\s*,\s*/,$query->param('mandatory')); my $cfldcnt = TTXCommon::cfldcnt(); foreach my $input (@inputs) { next if $input !~ /^x./; next if $query->param($input) eq undef; @mandatory = grep { $_ ne $input } @mandatory; my $val = $query->param($input); my $cidx; if ($cfg->get($input) ne undef) { $cidx = int ($cfg->get($input)); if ($cidx >= 0 && $cidx < $cfldcnt) { if ($cfg->get("c$cidx.type") eq 'list') { my @vals = $query->param($input); my @allow = split(/;/, $cfg->get("dropdown$cidx")); my @accept; foreach my $v (@vals) { push @accept, $v if grep(/^$v$/, @allow); } $val = join(';', @accept); $ticket->{"c$cidx"} = ";$val;" if $val ne undef; } else { $ticket->{"c$cidx"} = $val; } } } $input =~ s/^x//; $custom .= "[b]$input:[/b] $val\n"; } $custom .= "\n" if $custom ne undef; foreach my $mid (@mandatory) { $mid =~ s/^x//; $error .= '[%Missing%]'." $mid
\n"; } if ($error ne undef) { $error =~ s/
$//; $data->{ERROR_MESSAGE} = $error; kid($cfg, $query, $data, $useold) if $cfg->get('useaccesscode'); return undef; } foreach my $input ('email', 'name') { $cfg->set("_GLOBAL_newticket_$input", $query->param($input)); } TTXCommon::cleanit($query, 'problem'); my $tickets = TTXCommon::dbtik(); if ($tickets->error() ne undef || !$tickets->addticket($ticket)) { $data->{ERROR_MESSAGE} = $tickets->errortext(); kid($cfg, $query, $data, $useold) if $cfg->get('useaccesscode'); return undef; } if ($cfg->get('validate') ne undef) { my $n = $cfg->get('validate'); if ($n =~ /^\d\d?$/) { $ticket->{"c$n"} = 'N'; srand($ticket->{open}); my $k = int (rand(999999)); $data->{VALIDATIONCODE} = $ticket->{id}."Z$k"; } } my @messages; $messages[0] = "From: ".$ticket->{name}."\nDate: ".time()."\nIP: $ENV{REMOTE_ADDR}\n"; if ($query->param('internal')) { $messages[0] .= "Operator: 1\nInternal: Yes\n"; } if (TTXData::get('ISPRO')) { for (my $i = 1; $i < 4; ++$i) { next if $query->param("f$i") eq undef; $messages[0] .= TTXFile::addfile($query, "f$i", "fname$i", $ticket->{id}); } } my $autoassignmsg; if ($cfg->get('useopersel') && $query->param('oper') ne undef && $query->param('oper') ne '-') { my $u = TTXUser->new($query->param('oper')); if ($u ne undef) { $ticket->{oper} = $u->{login}; $ticket->{status} = 'OPN'; $autoassignmsg = '[%The ticket is assigned to%] '.$u->{fname}.' '.$u->{lname}."\n\n"; } } $messages[0] .= "\n$custom$autoassignmsg".substr($query->param('problem'),0,32*1024); $ticket->{messages} = \@messages; if (!$tickets->save()) { $data->{ERROR_MESSAGE} = $tickets->errortext(); kid($cfg, $query, $data, $useold) if $cfg->get('useaccesscode'); return undef; } unlink $cfn if $cfn ne undef && -f $cfn; $tickets = undef; $data->{TICKETID} = $ticket->{id}; $data->{TICKETKEY} = $ticket->{key}; usernotify({cfg => $cfg, ticket => $ticket, validationcode => $data->{VALIDATIONCODE}}) if !$query->param('internal'); if ($cfg->get('validate') eq undef) { my $opernotifydata = {cfg => $cfg, ticket => $ticket}; if ($cfg->get('supervisor') ne undef) { my @list = ($cfg->get('supervisor')); $opernotifydata->{notifylist} = \@list; } elsif ($cfg->get('usegrpsel')) { my @list = split(/;/, $cfg->get("mbr-".$ticket->{grp})); $opernotifydata->{notifylist} = \@list; } elsif ($cfg->get('routex') ne undef) { eval "use TTXCustomRoute"; if ($@ eq undef) { my @list = TTXCustomRoute::routelist($cfg, $ticket); $opernotifydata->{notifylist} = \@list; } } opernotify($opernotifydata); } if ($cfg->get('report')) { eval "use TTXEReport"; if ($@ eq undef) { TTXEReport::reportopen($cfg, $ticket); } } if ($cfg->get('internaldesk')) { $query->param(-name => 'reset', -value => 1); return 'helpdesk'; } if ($query->param('noconfirmation')) { $query->param(-name => 'reset', -value => 1); return 'helpdesk'; } return 'confirmnew'; } # =================================================================== confirmnew sub confirmnew { my ($cfg, $query, $data) = @_; return undef; } # ==================================================================== fid2title my $_f2t; sub fid2title { if ($_f2t eq undef) { eval 'use TTXData'; my $cfg; eval '$cfg = TTXData::get'."('CONFIG')"; return $_[0] if $cfg eq undef; my @xflds = grep(/^x/, $cfg->vars()); foreach my $xfld (@xflds) { my $t = $xfld; $t =~ s/^x//; $_f2t->{'c'.$cfg->get($xfld)} = $t; } } return $_f2t->{$_[0]} || $_[0]; } # ======================================================================= wanted sub wanted { my ($cfg, $query, $ticket) = @_; my $buff; if ($cfg->get('mandatoryfields') ne undef) { my @mflist = split(/;/, $cfg->get('mandatoryfields')); foreach my $fid (@mflist) { next if $fid !~ /^c\d+$/; my $title = fid2title($fid); $title =~ s/\n$title\n"; my $id = $fid; $id =~ s/^c//; if ($cfg->get("dropdown$id") ne undef) { $buff .= "\n"; } else { my $val = $query->param('do') ? $query->param("wanted$id") : $ticket->{$fid}; $val =~ s/"/"/g; $buff .= "\n"; } $buff .= "\n\n"; } if ($buff ne undef) { $buff = "\n". "\n\n\n". "
\n". "$buff
\n". "
\n"; } } return $buff; } # ======================================================================= lockdb my $lockcnt; sub lockdb { return ++$lockcnt if $lockcnt; my $cfg = TTXData::get('CONFIG'); return 1 if $cfg->get('dbmode') !~ /sql/; my $lockfile = $cfg->get('basedir')."/lockdb.ttx"; if (! -f $lockfile) { if (!open(LOCK, ">$lockfile")) { return 0; } } elsif (!open(LOCK,"+<$lockfile")) { return 0; } flock(LOCK, 2); $lockcnt = 1; return 1; } # ===================================================================== unlockdb sub unlockdb { return if !$lockcnt; my $cfg = TTXData::get('CONFIG'); return if $cfg->get('dbmode') !~ /sql/; --$lockcnt; if (!$lockcnt) { flock(LOCK, 8); close LOCK; } } # ======================================================================== claim sub claim { my ($cfg, $query, $data) = @_; if ($query->param('cancel') ne undef) { $query->param(-name => 'tid', -value=> ''); $query->param(-name => 'do', -value => ''); return 'helpdesk'; } return undef if !$query->param('do'); $query->param(-name => 'do', -value => ''); lockdb(); my $tickets = TTXCommon::dbtik(); if ($tickets eq undef || $tickets->error() ne undef) { $data->{ERROR_MESSAGE} = $tickets->errortext(); unlockdb(); return undef; } my $t = $tickets->ticketbykey(TTXCommon::cleanit($query, 'key')); if ($t eq undef) { $data->{ERROR_MESSAGE} = '[%Invalid access key%]'; $query->param(-name => 'tid', -value=> ''); unlockdb(); return 'helpdesk'; } if ($t->{oper} ne undef) { $data->{ERROR_MESSAGE} = '[%The ticket is already assigned to%] '.$t->{oper}; } else { if ($cfg->get('mandatoryfields') ne undef && $query->param('delete') eq undef) { my @mflist = split(/;/, $cfg->get('mandatoryfields')); my $err; foreach my $fid (@mflist) { next if $fid !~ /^c\d+$/; my $id = $fid; $id =~ s/^c//; if ($query->param("wanted$id") ne undef) { $t->{$fid} = $query->param("wanted$id"); } elsif ($t->{$fid} eq undef) { $err .= '[%Missing%] '.fid2title($fid)."
"; } } if ($err ne undef) { $data->{ERROR_MESSAGE} = $err; $data->{WANTED} = wanted($cfg, $query, $t); unlockdb(); return 'ticket'; } } $t->{oper} = $cfg->get('_USER')->get('login'); $t->{status} = 'OPN'; if ($cfg->get('claimtime') ne undef) { my $totaltm = int ((time() - $t->{open})/60); my $totaltmm = $totaltm % 60; my $totaltmh = int(($totaltm - $totaltmm) / 60); $totaltmm = "0$totaltmm" if $totaltmm < 10; $totaltmh = "0$totaltmh" if $totaltmh < 10; $t->{'c'.$cfg->get('claimtime')} = $totaltmh.':'.$totaltmm; } $tickets->save(); TTXCommon::logit("CLAIM|".$t->{oper}."|".$t->{id}); if ($query->param('delete') ne undef) { my $oper = $cfg->get('_USER')->get('login'); if (!($cfg->get('demo') && $oper ne 'admin') && ($cfg->get('grant.delete') eq undef || grep(/^$oper$/, split(/,/, $cfg->get('grant.delete'))))) { $tickets->deltik($t->{id}); $tickets->save(); } $query->param(-name => 'tid', -value=> ''); unlockdb(); return 'helpdesk'; } } unlockdb(); return 'ticket'; } # ======================================================================= ticket sub ticket { my ($cfg, $query, $data) = @_; checkgroups($cfg); TTXCommon::tickedvars($cfg, $data); if ($query->param('cancel') ne undef) { if ($query->param('sid') ne undef) { $query->param(-name => 'tid', -value=> ''); $query->param(-name => 'do', -value=> ''); return 'helpdesk'; } else { if ($query->param('emailkey') ne undef) { return 'mytickets'; } else { return 'newticket'; } } } if ($query->param('key') eq undef) { return 'keyform'; } my $tickets = TTXCommon::dbtik(); if ($tickets eq undef || $tickets->error() ne undef) { $data->{ERROR_MESSAGE} = $tickets->errortext(); return undef; } my $t = $tickets->ticketbykey(TTXCommon::cleanit($query, 'key')); if ($t eq undef) { $data->{ERROR_MESSAGE} = $cfg->get('shortkey') ? '[%Ticket does not exist%]':'[%Invalid access key%]'; return 'keyform'; } $query->param(-name => 'tid', -value=> $t->{id}); my $cmd; my $isoper = 0; my $readonly; if ($cfg->get('_USER') ne undef) { $readonly = $cfg->get('_USER')->get('ro') ? 1:0; if (!$readonly) { my $oper = $cfg->get('_USER')->get('login'); my $candel; if (!($cfg->get('demo') && $oper ne 'admin') && ($cfg->get('grant.delete') eq undef || grep(/^$oper$/, split(/,/, $cfg->get('grant.delete'))))) { $candel = 1; } if ($t->{oper} eq undef) { $cmd = 'claim'; $data->{WANTED} = wanted($cfg, $query, $t); if ($candel) { $data->{DELTICKET} = '  '; } } else { if ($candel) { $data->{DELTICKET} = ''; } } $data->{TICKEDOPEN} = "param('tid')."')\">"; $data->{TICKEDCLOSE} = ''; $data->{ANSWERLIB} = '    [%Answer library%]'; } $isoper = 1; } if ($query->param('do') && $t->{status} eq 'CLS' && !$isoper) { $query->param(-name => 'email', -value => $t->{email}); $query->param(-name => 'name', -value => $t->{name}); $query->param(-name => 'subject', -value => $t->{subject}); $query->param(-name => 'problem', -value => TTXDictionary::translate('Posted as a followup to the solved ticket').' #'.$t->{id}. ".\n\n".$query->param('problem')); return 'newticket'; } if ($t->{status} =~ /OPN|WFR/ && !$readonly) { $data->{PREVIEW} .= '  [%Preview%]'; } my $notify; my $xupdate = 0; my @inputs = $query->param(); @inputs = grep(/^x/, @inputs); my $cfldcnt = TTXCommon::cfldcnt(); foreach my $input (@inputs) { my $val = TTXCommon::cleanit($query, $input); my $n = $cfg->get($input); next if $n eq undef || $n =~ /[^0-9]/ || $n < 0 || $n >= $cfldcnt; if ($val ne $t->{"c$n"}) { $t->{"c$n"} = $val; $xupdate = 1; } } if ($cmd eq undef && $query->param('do')) { if ($readonly) { $query->param(-name => 'do', -value=> ''); $query->param(-name => 'tid', -value=> ''); return 'helpdesk'; } if ($isoper && $query->param('delete') ne undef) { my $oper = $cfg->get('_USER')->get('login'); if (!($cfg->get('demo') && $oper ne 'admin') && ($cfg->get('grant.delete') eq undef || grep(/^$oper$/, split(/,/, $cfg->get('grant.delete'))))) { $tickets->deltik($t->{id}); $tickets->save(); } $query->param(-name => 'do', -value=> ''); $query->param(-name => 'tid', -value=> ''); return 'helpdesk'; } my $attachment; if (TTXData::get('ISPRO')) { for (my $i = 1; $i < 4; ++$i) { next if $query->param("f$i") eq undef; $attachment .= TTXFile::addfile($query, "f$i", "fname$i", $t->{id}); } } if ($query->param('problem') eq undef && $attachment ne undef) { $query->param(-name => 'problem', -value => 'See attachment'); } if ($query->param('problem') eq undef && $query->param('newstatus') eq 'CLS') { $query->param(-name => 'problem', -value => TTXDictionary::translate('Ticket closed')); } if ($query->param('problem') eq undef && ($query->param('newstatus') eq undef || $t->{status} eq $query->param('newstatus')) && ($query->param('transfer') eq undef || $t->{oper} eq $query->param('transfer')) && !$xupdate) { $data->{ERROR_MESSAGE} = '[%Missing message%]'; } elsif (!$isoper && $cfg->get('askforname') && TTXCommon::cleanit($query, 'respondername') eq undef) { $data->{ERROR_MESSAGE} = '[%Missing name%]'; } else { if ($isoper && $cfg->get('tracktime') ne undef) { my $ttx = $cfg->get('tracktime'); if ($ttx ne undef && $ttx !~ /[^0-9]/ && $ttx >= 0 && $ttx < $cfldcnt) { my $tth = int(TTXCommon::cleanit($query, 'tracktimeh')); my $ttm = int(TTXCommon::cleanit($query, 'tracktimem')); $query->param(-name => 'tracktimeh', -value => ''); $query->param(-name => 'tracktimem', -value => ''); my ($totaltmh, $totaltmm) = split(/:/, $t->{"c$ttx"}); my $totaltm = $totaltmh * 60 + $totaltmm; $totaltm += $tth * 60 + $ttm; $totaltmm = $totaltm % 60; $totaltmh = int(($totaltm - $totaltmm) / 60); $totaltmm = "0$totaltmm" if $totaltmm < 10; $totaltmh = "0$totaltmh" if $totaltmh < 10; $t->{"c$ttx"} = $totaltmh.':'.$totaltmm; } } if ($query->param('newstatus') ne undef) { $t->{status} = $query->param('newstatus'); } else { if ($t->{status} eq 'OPN' && $isoper && !$query->param('internal')) { $t->{status} = 'WFR'; } if ($t->{status} eq 'WFR' && !$isoper) { $t->{status} = 'OPN'; } } if ($query->param('problem') ne undef || $attachment ne undef) { my $unm = $t->{name}; if ($cfg->get('askforname')) { $unm = $query->param('respondername'); } my $msg = "From: ".($isoper ? $cfg->get('_USER')->get('login') : $unm)."\nDate: ".time()."\nOperator: $isoper\n"; if ($isoper && $query->param('internal')) { $msg .= "Internal: Yes\n"; } $msg .= "IP: $ENV{REMOTE_ADDR}\n"; $msg .= "$attachment\n".substr($query->param('problem'),0,32*1024); push @{$t->{messages}}, $msg; $query->param(-name => 'problem', -value => ''); $notify = 1; if ($isoper) { TTXCommon::logit("RESPOND|".$t->{oper}."|".$t->{id}); } } if ($isoper && $query->param('transfer') ne undef && $query->param('transfer') ne $t->{oper}) { TTXCommon::logit("TRANSFER|".$t->{oper}."|".$t->{id}."|".$query->param('transfer')); if ($query->param('transfer') !~ /^\+/) { $t->{oper} = $query->param('transfer'); $t->{status} = 'OPN' if $cfg->get('transferopens'); transfernotice({cfg => $cfg, ticket => $t}); } elsif($cfg->get('usegrpsel')) { $t->{grp} = $query->param('transfer'); TTXGroups::grptransfer({cfg => $cfg, ticket => $t}); } } $t->{updated} = time(); if ($t->{status} eq 'CLS') { $t->{closed} = $t->{updated}; if ($isoper) { TTXCommon::logit("CLOSE|".$t->{oper}."|".$t->{id}); if ($cfg->get('report') ne undef) { eval "use TTXEReport"; if ($@ eq undef) { TTXEReport::reportclose($cfg, $t); } } } } $tickets->save(); if ($t->{status} eq 'PND' && $isoper) { $query->param(-name => 'do', -value => ''); # $query->param(-name => 'tid', -value => $t->{id}); my $oper = $cfg->get('_USER')->get('login'); if (!($cfg->get('demo') && $oper ne 'admin') && ($cfg->get('grant.delete') eq undef || grep(/^$oper$/, split(/,/, $cfg->get('grant.delete'))))) { $data->{DELTICKET} = '  '; } # usernotify({cfg => $cfg, ticket => $t}) if $notify && !($isoper && $query->param('internal')); $cmd = 'claim'; } } } $tickets = undef; if ($isoper && invenabled()) { $data->{INVENTORYROW} = TTXInvMod::invrow($cfg, $data, $t); } if (($t->{status} eq 'WFR' || $t->{status} eq 'CLS') && $isoper) { $data->{NEWSTATUS} = "param('newstatus') eq 'OPN') { $data->{NEWSTATUS} .= ' checked'; } $data->{NEWSTATUS} .= '> [%Open%]
'."\n"; } if ($t->{status} eq 'OPN') { $data->{NEWSTATUS} .= "param('newstatus') eq 'WFR') { $data->{NEWSTATUS} .= ' checked'; } $data->{NEWSTATUS} .= '> [%Responded%]
'."\n"; } if ($t->{status} ne 'CLS') { $data->{NEWSTATUS} .= "param('newstatus') eq 'CLS') { $data->{NEWSTATUS} .= ' checked'; } $data->{NEWSTATUS} .= '> [%Closed%]
'."\n"; } if ($isoper) { $data->{NEWSTATUS} .= '
param('internal')) { $data->{NEWSTATUS} .= ' checked'; } $data->{NEWSTATUS} .= '> [%Internal%]
'; } my $userid = $cfg->get('_USER')->{login} if $isoper; if ($isoper && ($userid eq $t->{oper} || grep(/^$userid$/, split(/,/, $cfg->get('grant.assign'))))) { my @operlist = TTXUser::list(); # if ($cfg->get('usegrpsel') && $cfg->get('accessctrl')) { if ($cfg->get('usegrpsel')) { my $rev; eval '$rev = $TTXGroups::REVISION'; # if ($@ eq undef && $rev > 213) { if ($@ eq undef && $rev > 231) { TTXGroups::cleanoperlist($cfg, \@operlist, $t->{grp}); } } if (@operlist > 1) { $data->{NEWSTATUS} .= '
'."\n". "
[%Transfer to%]

\n"; } } $data->{PAGEHEADING} = '[%Ticket%] #'.$t->{id}; $data->{TICKET_status} = TTXCommon::status($t->{status}); $data->{TICKET_subject} = $t->{subject}; $data->{TICKET_email} = $t->{email}; $data->{TICKET_name} = $t->{name}; $data->{TICKET_key} = $t->{key}; $data->{TICKET_open} = TTXCommon::tmtxt($t->{open}); $data->{TICKET_GROUP} = $cfg->get($t->{grp}) || '-'; $data->{TICKET_closed} = $t->{status} eq 'CLS' ? TTXCommon::tmtxt($t->{closed}):'-'; $data->{TICKET_oper} = $isoper ? $t->{oper}:(($t->{oper} eq undef) ? '[%not assigned%]':'[%assigned%]'); my @xvars = $cfg->vars(); @xvars = grep(/^x/, @xvars); foreach my $xvar (@xvars) { $data->{'TICKET_c'.$cfg->get($xvar)} = $t->{'c'.$cfg->get($xvar)}; if ($query->param('do') eq undef) { $query->param(-name => $xvar, -value => $t->{'c'.$cfg->get($xvar)}); } } if ($cfg->get('tracktime') ne undef && $isoper) { my $ttx = $cfg->get('tracktime'); if ($ttx !~ /[^0-9]/ && $ttx >= 0 && $ttx < $cfldcnt) { my $tth = TTXCommon::cleanit($query, 'tracktimeh'); my $ttm = TTXCommon::cleanit($query, 'tracktimem'); $tth =~ s/"/"/g; $ttm =~ s/"/"/g; $data->{TRACKTIME} = '[%Time spent on preparing the response (hh:mm)%]'."\n". ":". "

"; } } if (!$isoper && $cfg->get('askforname')) { if ($query->param('emailkey') ne undef && $query->param('emailkey') eq $t->{email}) { if ($query->param('respondername') eq undef) { $query->param(-name => 'respondername', -value => $t->{name}); } } my $nm = $query->param('respondername'); $nm =~ s/"/"/g; $data->{TRACKTIME} .= '[%Your name%]:  '. "\n

"; } my %images; my %names; my $editlink; if ($isoper) { my $uid = $cfg->get('_USER')->get('login'); if ($cfg->get('grand.edit') eq undef || grep(/^$uid$/, split(/,/, $cfg->get('grand.edit')))) { eval 'use TTXMsgEdit'; if ($@ eq undef) { $editlink = 1; } } } my $mid = 0; foreach my $msg (@{$t->{messages}}) { my @msgparts = split(/\n\n/, $msg); my $rawheader = shift @msgparts; my $body = join("\n\n", @msgparts); my @headerlines = split(/\n/, $rawheader); chomp @headerlines; my %header; foreach my $line (@headerlines) { if ($line =~ /^([a-zA-Z][a-zA-Z0-9-]*):\s*(.*)$/) { $header{uc $1} = $2; } } next if $header{OPERATOR} && $header{INTERNAL} =~ /Yes/ && !$isoper; my $style; if ($header{OPERATOR}) { if ($header{INTERNAL} =~ /Yes/) { $style = 'imsg'; } else { $style = 'omsg' } } else { $style = 'cmsg'; } my $imgtag; my $fromtag; if ($header{OPERATOR}) { if (!exists $images{$header{FROM}}) { my $u = TTXUser->new($header{FROM}); if ($u eq undef) { $images{$header{FROM}} = undef; $names{$header{FROM}} = undef; } else { $images{$header{FROM}} = $u->{image}; $names{$header{FROM}} = $u->{fname}.' '.$u->{lname}; } } if ($images{$header{FROM}} ne undef) { $imgtag = "

  

\n"; } $fromtag = $names{$header{FROM}}; if ($isoper) { $fromtag .= " ($header{FROM})"; } } $fromtag = $header{FROM} if $fromtag eq undef; $fromtag =~ s/[$header{IP}]"; } } if ($editlink ne undef) { $editlink = "
".TTXMsgEdit::link($cfg, $t, $mid); ++$mid; } $data->{MESSAGES} .= "\n$fromtag
".TTXCommon::tmtxt($header{DATE})."$editlink$imgtag\n"; $body =~ s/\n/g; my $firstfile = 1; for (my $i=1; $i < 4; ++$i) { if ($header{"FILE$i"} ne undef) { if ($firstfile) { $body .= '

[%Attachments%]:
'."\n"; $firstfile = 0; } $header{"FILE$i"} =~ s/CGIURL/$ENV{SCRIPT_NAME}/; $body .= $header{"FILE$i"} . "
\n"; } } $data->{MESSAGES} .= "$body\n\n"; } if (TTXData::get('ISPRO')) { $data->{FILEFORMS} = TTXFile::fileforms(); } if ($notify) { if (!($isoper && $query->param('internal'))) { usernotify({cfg => $cfg, ticket => $t, from => $isoper ? $cfg->get('_USER')->{fname}.' '.$cfg->get('_USER')->{lname} : $query->param('respondername')}); if ($cfg->get('askforsurvey') && $t->{status} eq 'CLS') { eval "use TTXSurvey"; TTXSurvey::ask($cfg, $t); } } if (!$isoper) { if ($t->{status} eq 'PND') { if ($cfg->get('validate') eq undef || $t->{'c'.$cfg->get('validate')} eq 'Y') { my $opernotifydata = {cfg => $cfg, ticket => $t}; if ($cfg->get('supervisor') ne undef) { my @list = ($cfg->get('supervisor')); $opernotifydata->{notifylist} = \@list; } elsif ($cfg->get('usegrpsel')) { my @list = split(/;/, $cfg->get("mbr-".$t->{grp})); $opernotifydata->{notifylist} = \@list; } elsif ($cfg->get('routex') ne undef) { eval "use TTXCustomRoute"; if ($@ eq undef) { my @list = TTXCustomRoute::routelist($cfg, $t); $opernotifydata->{notifylist} = \@list; } } opernotify($opernotifydata); } } else { opernotify({cfg => $cfg, ticket => $t}); } } elsif ($t->{oper} ne undef && $cfg->get('_USER')->{login} ne $t->{oper}) { opernotify({cfg => $cfg, ticket => $t}); } } return $cmd; } 1; #