package TTXSetup; # # 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) $ # $TTXSetup::VERSION='2.22'; BEGIN { $TTXSetup::REVISION = '$Revision: 287 $'; if ($TTXSetup::REVISION =~ /(\d+)/) { $TTXSetup::REVISION = $1; } }; use TTXConfig; require TTXCommon; require TTXUser; use strict; my $cfg = {}; my $query = {}; my $ttpreview = < [%Message Preview%]
(%PAGEHEADING%)


(%MESSAGE%)


EOT my $ttshowitem = < (%ITEMLABEL%) (%ITEM_TITLE%)
(%ERROR_MESSAGE%)(%MESSAGE%)
(%ITEM_TITLE%)
[%Status%]: (%STATUS_SELBOX%)
[%Notes%]
(%ITEM_QNOTES%)
[%add notes%]
EOT my $ttdashboard = < (%ITEMLIST%)
EOT my $ttlogin = <
(%INPUT_loginmsg%)

[%User ID%]
[%Password%]


EOT my $ttorderalib =< Answer library
Answer Library module is not installed on your server.

The module allows organizing answers to common questions. Your staff can quickly access canned answers for instant solutions that save problem-resolution time. New staff members will benefit from the canned answer library for training, enabling them to hit the ground running and be productive right from their first day.

You may see the Answer Library module in action through our demo help desk.

For more detailed description and ordering info please refer to Answer Library page. EOT my $ttheader =< (%CONFIG_COMPANY%)
(%LOGGEDAS%) (%HOMELINK%) (%DASHBOARDLINK%) [%Tickets%] | [%Contact us%] | (%LOGINLOGOUT%)
(%PAGEHEADING%)

(%ERROR_BOX%) EOT my $ttfooter =< Powered by Trouble Ticket Express rev. (%VERSION%) (%ISPRO%) (%CHECKUPDATE%)
EOT my $tthelpdesk =<
 
(%TICKETFILTER%)
 

[%Show%] [%tickets per page.%] [%Include abstracts%]  

(%TICKETLIST%)

EOT my $ttreports =<
  (%REPORTQUERY%)  

(%REPORT%)

EOT my $ttmarkuphelp =< [%Markup Quick Guide%]
[b] Sample text [/b] Sample text
[i] Sample text [/i] Sample text
[u] Sample text [/u] Sample text
[code] Sample text [/code] Sample text
[url http://www.troubleticketexpress.com]Trouble Ticket Express[/url] Trouble Ticket Express
[img http://www.helpdeskconnect.com/i/sml.gif]
[(] [
EOT my $ttroticket =< [%Subject%]    (%TICKEDOPEN%)(%TICKET_subject%)(%TICKEDCLOSE%) [%Group%]    (%TICKET_GROUP%) [%Status%]    (%TICKET_status%) [%Operator%]    (%TICKET_oper%) [%Created%]    (%TICKET_open%) [%Customer%]    (%TICKET_name%) ((%TICKET_email%)) [%Solved%]    (%TICKET_closed%) [%Access key%]    (%TICKET_key%)  
(%MESSAGES%)
EOT my $ttticket =< (%INVENTORYROW%)
 
[%Subject%]    (%TICKEDOPEN%)(%TICKET_subject%)(%TICKEDCLOSE%) [%Group%]    (%TICKET_GROUP%)
[%Status%]    [%(%TICKET_status%)%] [%Operator%]    (%TICKET_oper%)
[%Created%]    (%TICKET_open%) [%Customer%]    (%TICKET_name%) ((%TICKET_email%))
[%Solved%]    (%TICKET_closed%) [%Access key%]    (%TICKET_key%)
 

(%MESSAGES%)
 

[%New status%]
(%NEWSTATUS%)

 [%Message%](%ANSWERLIB%)(%PREVIEW%)   [%Markup help%]

(%FILEFORMS%)
(%TRACKTIME%) (%DELTICKET%)

EOT my $ttnewticket =<

(%CUSTOMVARS%) (%ITEMSELSTD%) (%OPERSELSTD%) (%GROUPSELSTD%) (%KIDFORM%)
[%Name%]*
[%Email%]*
[%Subject%]*
[%Problem%]   [%preview%]
(%FILEFORMS%)
(%INTERNALCHECKBOX%)

EOT my $ttconfirmnew =<
  We have received your message and created a service ticket #(%TICKETID%). Please refer to this number in all follow-up communications relating to this specific issue.

Note: We have sent a confirmation email to (%INPUT_email%) including instructions for tracking status of your ticket. If you did not receive the confirmation shortly after submission of your inquiry, there is something wrong with the email address you provided. Please be advised that if we cannot deliver a confirmation, we cannot deliver an answer as well. In an event of email delivery problems please use our online system in order to fetch an answer.

In order to access your service request via our online system, please use the following access key:

(%TICKETKEY%)

or follow this link

Ticket #(%TICKETID%) (do not forget to bookmark it)

(both key and link have been sent to your email address (%INPUT_email%) as well)

 

EOT my $ttclaim =<
 
[%Status%]    [%(%TICKET_status%)%]
[%Subject%]    (%TICKET_subject%)
[%Group%]    (%TICKET_GROUP%)
[%Created%]    (%TICKET_open%)
 

(%MESSAGES%)

(%WANTED%)


(%DELTICKET%)  


EOT my $ttanswerlib =< [%Answer library%] (%INITANSWERS%)
(%ANSWERLIBERROR%) (%ANSWERLIBMSG%)
1. [%Select%]

    
2. [%Preview%]

3. [%Copy%] =>
     
EOT my $ttnewanswer =< [%Answer editor%]
(%ANSWERLIBERROR%) (%ANSWERLIBMSG%)
[%Title%]
[%Select existing category%]
[%or define new one%]:
[%Answer%]
(%COMMONCHECKBOX%)
EOT my $ttdelanswer =< [%Deleting answer%]

(%ANSWERLIBERROR%) (%ANSWERLIBMSG%)
[%Deleting answer%]

(%ANSWERTITLE%)

[%Are you sure?%]

EOT my $ttkeyform =<


[%Access Key%]



(%MESSAGE%)


[%Email address%]



EOT my $ttnewmessage =< [%Ticket Editor%]
(%ERROR_MESSAGE%)(%MESSAGE%) (%CUSTOMFIELDS%)
[%Ticket ID%] (%INPUT_tid%)
[%Email%]
[%Name%]
[%Subject%]
EOT my $ttmsgedit =< [%Message Editor%]
(%ERROR_MESSAGE%)(%MESSAGE%)


EOT my @templates = ( {name => 'login.html', data => $ttlogin}, {name => 'header.shtml', data => $ttheader}, {name => 'footer.shtml', data => $ttfooter}, {name => 'helpdesk.html', data => $tthelpdesk}, {name => 'ticket.html', data => $ttticket}, {name => 'roticket.html', data => $ttroticket}, {name => 'newticket.html', data => $ttnewticket}, {name => 'confirmnew.html', data => $ttconfirmnew}, {name => 'claim.html', data => $ttclaim}, {name => 'keyform.html', data => $ttkeyform}, {name => 'newmessage.txt', data => $ttnewmessage}, {name => 'newticket.txt', data => $ttnewticketeml}, {name => 'onewmessage.txt', data => $ttonewmessage}, {name => 'onewticket.txt', data => $ttonewticket}, {name => 'orderalib.html', data => $ttorderalib}, {name => 'markuphelp.html', data => $ttmarkuphelp}, {name => 'answerlib.html', data => $ttanswerlib}, {name => 'delanswer.html', data => $ttdelanswer}, {name => 'newanswer.html', data => $ttnewanswer}, {name => 'ticketed.html', data => $ttticketed}, {name => 'reports.html', data => $ttreports}, {name => 'preview.html', data => $ttpreview}, {name => 'showitem.html', data => $ttshowitem}, {name => 'dashboard.html', data => $ttdashboard}, {name => 'msgedit.html', data => $ttmsgedit}, ); # ======================================================================= setup1 sub setup1 { $cfg = $_[0]; $query = $_[1]; my $msg = $_[2]; my $error = "
Error: $msg

" if $msg ne undef; my $sd = scriptdir(); my $pwd = $query->param('pwd'); my $basedir = $query->param('basedir'); print <

Welcome to Trouble Ticket Express Setup Wizard

Step 1. Setting Data Directory

Trouble Ticket Express requires read/write access to some directory in order to store its data files. The directory must exist and be writeable to Trouble Ticket Express scripts. Default settings assume usage of the directory you have uploaded the script to, that is

$sd

EOT if (! -w $sd) { print <Please be advised that some hosting providers do not allow setting 775 and/or 777 access rights for scripts directory, in such a case you will see Internal Server Error page after clicking Retry button. You will need to restore access rights for the directory to 755 and run setup again.
EOT } else { print <Please be advised that some hosting providers do not allow scripts to write data into scripts directory, in such a case you will see Internal Server Error page after clicking on "Use Default Data Directory" button. You will need to restart Setup Wizard and specify alternate data directory.
EOT } print <alternate Data Directory please enter absolute filesystem pathname of the directory. The directory must exist and be writeable to Trouble Ticket Express scripts (set access rights to 777 on Unix). Please be advised that filesystem pathname is not an URL, that is

/home/www.mysite.com/htdocs/datadir is a filesystem path, while

/datadir and http://www.mysite.com/datadir - are URLs
$error
Data Directory:

EOT } # ======================================================================= setup2 sub setup2 { my $microsoft = $^O eq 'MSWin32'; $cfg = $_[0]; $query = $_[1]; my $dir = $query->param('basedir'); $dir =~ s/\/$//; if ($dir eq undef) { setup1($_[0], $_[1], "No directory provided"); return; } if (!$microsoft && $dir !~ /^\//) { setup1($_[0], $_[1], "The directory is not absolute (must begin with '/')"); return; } if (! -e $dir) { setup1($_[0], $_[1], "Directory $dir does not exist"); return; } if (! -d $dir) { setup1($_[0], $_[1], "The $dir is a FILE"); return; } if (! -w $dir) { setup1($_[0], $_[1], "The $dir is not writeable"); return; } my $cf = "$dir/ttxcfg.cgi"; if ( -e $cf && ! -w $cf) { setup1($_[0], $_[1], "The $dir already contains congif file, which is not writeable"); return; } $cfg->set('basedir', $dir); $cfg->set('ttx.version', $TTXSetup::VERSION); # $cfg->set('usecache', 1); $cfg->file($cf); if (!$cfg->save()) { setup1($_[0], $_[1], "Error saving config file"); return; } build($_[0]); my $scrdir = scriptdir(); if ($dir eq $scrdir) { print "

Congratulations! Initial setup completed

". "
"; } else { my $dirinfo = 'ttxcfg.cgi'; print <
Configuration file was recorded to

$dir


To complete setup please

1. Create text file $dirinfo

2. Copy and paste this text into the $dirinfo file.



3. Upload $dirinfo file into $scrdir directory.

4. Warning: Skipping steps 1-3 will restart Setup Wizard.
EOT } } # ======================================================================= setup3 sub setup3 { $cfg = $_[0]; $query = $_[1]; my $error; if ($query->param('do')) { if ($query->param('admpwd') eq undef) { $error = 'Missing admin password'; } elsif ($query->param('admpwd') ne $query->param('admpwd1')) { $error = 'Passwords do not match'; } else { $cfg->set('admpwd', $query->param('admpwd')); $cfg->save(); $query->param(-name => 'pwd', -value => $query->param('admpwd')); $query->param(-name => 'do', -value => ''); setup4($cfg, $query); return; } } $error = "
Error: $error

" if $error ne undef; print <Administrator Password

This password is valid for Setup utility only. Assigning an administrator password will not create any operator accounts. You will be provided user management menu later during setup.

$error
Enter password
Repeat password
EOT } # ======================================================================= setup4 sub setup4 { $cfg = $_[0]; $query = $_[1]; my $pwd = $query->param('pwd'); my ($mailer, $smtp, $home, $company, $email, $imgurl, $emailfix, $smtptrace, $smtplogin, $smtppwd, $hideold, $useaccesscode, $nomsgcfrm, $notikcfrm ); if (!$query->param('do')) { $mailer = $query->param('mailer') || $cfg->get('mailer'); $smtp = $query->param('smtp') || $cfg->get('smtp'); $home = $query->param('home') || $cfg->get('home'); $company = $query->param('company') || $cfg->get('company'); $email = $query->param('email') || $cfg->get('email'); $imgurl = $query->param('imgurl') || $cfg->get('imgurl'); $emailfix = $query->param('emailfix') || $cfg->get('emailfix'); $smtptrace = $query->param('smtptrace') || $cfg->get('smtptrace'); $nomsgcfrm = $query->param('nomsgcfrm') || $cfg->get('nomsgcfrm'); $notikcfrm = $query->param('notikcfrm') || $cfg->get('notikcfrm'); $smtplogin = $query->param('smtplogin') || $cfg->get('smtplogin'); $smtppwd = $query->param('smtppwd') || $cfg->get('smtppwd'); $hideold = $query->param('hideold') || $cfg->get('hideoldsolved'); $useaccesscode = $query->param('useaccesscode') || $cfg->get('useaccesscode'); if ($cfg->get('blockautoresponder')) { $notikcfrm = 1; $nomsgcfrm = 1; } } else { $mailer = $query->param('mailer'); $smtp = $query->param('smtp'); $home = $query->param('home'); $company = $query->param('company'); $email = $query->param('email'); $imgurl = $query->param('imgurl'); $emailfix = $query->param('emailfix'); $smtptrace = $query->param('smtptrace'); $nomsgcfrm = $query->param('nomsgcfrm'); $notikcfrm = $query->param('notikcfrm'); $smtplogin = $query->param('smtplogin'); $smtppwd = $query->param('smtppwd'); $hideold = $query->param('hideold'); $useaccesscode = $query->param('useaccesscode'); } if ($mailer =~ /\s-t\b/) { $mailer =~ s/\s-t\b/ /; $query->param(-name => 'mailer', -value => $mailer); } my $error; if ($mailer ne undef) { my $m = $mailer; $m =~ s/^\s+//; $m =~ s/\s.*$//; if (! -f $m) { $error = "The $m program does not exist"; } elsif (! -x $m) { $error = "The $m is not executable"; } } if ($hideold =~ /\D/) { $hideold = undef; } $home =~ s/^\s*//; $home =~ s/\s*$//; if ($home ne undef && $home !~ /^https?:\/\//) { $home = 'http://'.$home; } my $emailfixchecked = 'checked' if $emailfix; my $smtptracechecked = 'checked' if $smtptrace; my $nmcchecked = 'checked' if $nomsgcfrm; my $ntcchecked = 'checked' if $notikcfrm; my $datadir = $cfg->get('basedir'); my $smtptracecmd; if ($smtptrace) { $smtptracecmd = "   ". "View log\n"; } $home =~ s/"/"/g; $company =~ s/"/"/g; my $tz = $query->param('timezone'); $tz = $cfg->get('timezone') if $tz eq undef; $tz = int $tz; my $layout = 0; my $layouthtml; eval "use TTXLayout"; if ($@ eq undef) { $layout = 1; } if ($query->param('do')) { $cfg->set('mailer', $mailer); $cfg->set('smtp', $smtp); $cfg->set('home', $home); $cfg->set('company', $company); $cfg->set('email', $email); $cfg->set('imgurl', $imgurl); $cfg->set('timezone', $tz); $cfg->set('smtplogin', $smtplogin); $cfg->set('smtppwd', $smtppwd); $cfg->set('emailfix', $emailfix ? 1:0); $cfg->set('smtptrace', $smtptrace ? 1:0); $cfg->set('nomsgcfrm', $nomsgcfrm ? 1:0); $cfg->set('notikcfrm', $notikcfrm ? 1:0); $cfg->set('hideoldsolved', $hideold); $cfg->set('useaccesscode', $useaccesscode); $cfg->set('blockautoresponder', '') if $cfg->get('blockautoresponder'); if ($layout) { TTXLayout::set($cfg, $query); } my $savecnt = $cfg->get('savecnt'); $cfg->set('savecnt', $savecnt + 1); $cfg->save(); build($cfg) if $query->param('tmpreset'); my $smtplog = $cfg->get('basedir').'/smtptrace.txt'; if (!$smtptrace) { if (-f $smtplog) { unlink $smtplog; } } if (!$savecnt) { $query->param(-name => 'do', -value => ''); setup5($cfg, $query); return; } } if ($layout) { $layouthtml = TTXLayout::html($cfg, $query); } else { $layouthtml =<

Ticket Browser Layout

Important: Your Trouble Ticket Express system does not have an optional Layout Designer module. This module is required in order to activate the Ticket Browser Layout section of setup form. Now this section is inactive, any changes to column and filter fields will be ignored. You may order the optional Layout Designer module at any time. Installation of the module is as simple as uploading a single file to your Trouble Ticket Express scripts directory, no existing data will be affected.

Column Show in browser Show in filter Allow editing
Text NA no
Ticket # no
Name yes
Email yes
Subject yes yes
Operator no
Status no
Created no no
Updated no no
Group no
Date Range NA no

Custom fields. Each ticket record has 10 custom fields (c0-c9). These fields may contain information, what is specific to your application. The intended usage is to hold values of contact form custom fields. This section allows you to define mapping of form fields to ticket fields. Your custom field titles must match input names as per your customized html template without leading 'x' character. Example: Enter 'Phone' to the box next to the c0 label if you want your custom contact form 'xPhone' values to be stored in custom field #0.
Note: This section is inactive now. Please order the optional Layout Designer module to activate the form.
c0:
c1:
c2:
c3:
c4:
c5:
c6:
c7:
c8:
c9:
EOT } $error = "
Error: $error

" if $error ne undef; print <System Info | Users | Groups | Inventory

System Setup

$error
EOT if ($^O =~ /mswin/i) { print < EOT eval 'use Authen::SASL'; if ($@ ne undef) { print < EOT } else { print < EOT } print < EOT } else { print < EOT print < EOT eval "use Au3then::SASL"; if ($@ ne undef) { print < EOT } else { print < EOT } print < EOT } my $checkuseaccesscode = ' checked' if $useaccesscode; my $gdlibrary = 'Requires GD library that is not installed on your server.'; eval 'use GD'; if ($@ eq undef) { $gdlibrary = undef; } print <$layouthtml
Data Directory:  $datadir
Templates Directory:  $datadir/templates
Reset templates:   (this will restore default templates)
 

By default all *.gif and *.css files will be uploaded to Trouble Ticket Express scripts directory. Not all hosting providers allow serving image files from scripts directory. You may upload *.gif and *.css files to another directory, please adjust value for "Image Directory URL" field in such a case - it must contain absolute URL of image directory.

Image Directory URL:  
 

Email settings

SMTP host:   examples: 127.0.0.1, smtp.mysite.com

Your SMTP server may (or may not) require authentication (user id & password) in order to allow relaying email to other servers. Please contact the SMTP server admin regarding your SMTP server access policies.

Unfortunately your server does not have a Perl library, which is required to use SMTP authentication. The name of the library is Authen::SASL. In order to install the library use the following commands at the DOS prompt (in order to access DOS prompt click on Windows Start button, select Run..., type command and hit Enter)

C:\>ppm
ppm> install Authen::SASL

If you are unable to install the Authen::SASL module, the Trouble Ticket Express will try using old fashioned POP3 auth mode: old versions of SMTP servers did not have authorization feature and they were accepting outgoing email only from users, which have recently checked their mail boxes using POP3 protocol. If you provide POP3 user id and password info below, the Trouble Ticket Express will try to login via POP3 protocol to the host running your SMTP server prior to sending email. Please remember, this is just a workaround and it is not guaranteed to work.

POP3 user id:  
POP3 password:  

If you are required to use the SMTP authentication, please provide SMTP user id and password here:

SMTP user id:  
SMTP password:  

Miscellaneous email settings

Fix MTA:   Check this if email messages have clobbered lines.
SMTP Trace:   Check if you do not receive email.$smtptracecmd
No ticket confirmations:   Customers will not receive new ticket confirmations.
No message confirmations:   Customers will not receive copies of their own messages.


If your Windows server have a sendmail or sendmail compatible software installed, you may provide an absolute pathname of the sendmail program instead of specifying SMTP server info.

Sendmail path:  
Trouble Ticket Express uses sendmail program in order to send email messages. You need to provide absolute path to the sendmail program. EOT my @smp = listsendmail(); if (@smp > 0) { my $s = (@smp > 1 ? 's':''); my $are = (@smp > 1 ? 'are':'is'); print <
EOT foreach my $sm (@smp) { print "$sm
\n"; } print "
\n"; } print <sendmail or substitute program on your server.

Sendmail path:  

If you do not know location of the sendmail on your server you may try using SMTP server instead.
Hint: The first thing to try is 127.0.0.1. This way you will eventually access the local sendmail program, provided it was configured to accept network connections.

Remeber: There is no need to provide SMTP server info if you already provided absolute path to sendmail program on your server.
SMTP host:   examples: 127.0.0.1, smtp.mysite.com

Your SMTP server may (or may not) require authentication (user id & password) in order to allow relaying email to other servers. Please contact the SMTP server admin regarding your SMTP server access policies.

Unfortunately your server does not have a Perl library, which is required to use SMTP authentication. The name of the library is Authen::SASL. In order to install the library use the following commands at the shell prompt

# perl -MCPAN -e shell
cpan>> install Authen::SASL

If you are unable to install the Authen::SASL module, the Trouble Ticket Express will try using old fashioned POP3 auth mode: old versions of SMTP servers did not have authorization feature and they were accepting outgoing email only from users, which have recently checked their mail boxes using POP3 protocol. If you provide POP3 user id and password info below, the Trouble Ticket Express will try to login via POP3 protocol to the host running your SMTP server prior to sending email. Please remember, this is just a workaround and it is not guaranteed to work.

POP3 user id:  
POP3 password:  

If you are required to use the SMTP authentication, please provide SMTP user id and password here:

SMTP user id:  
SMTP password:  

Miscellaneous email settings

Fix MTA:   Check this if email messages have clobbered lines.
SMTP Trace:   Check if you do not receive email.$smtptracecmd
No ticket confirmations:   Customers will not receive new ticket confirmations.
No message confirmations:   Customers will not receive copies of their own messages.
 

Your company name and email address

Company name:

  

Company email address:  
Company home page:  
Time Zone:   GMT offset in minutes (EST=-300, PST=-480 CET=60)
 
 
Hide solved tickets after   days.
 
Use access code image   $gdlibrary
 



Login to Trouble Ticket Express
EOT } # ======================================================================= setup5 sub setup5 { $cfg = $_[0]; $query = $_[1]; my $newlogin = $query->param('newlogin'); my $newpasswd = $query->param('newpasswd'); my $newemail = $query->param('newemail'); my $newfname = $query->param('newfname'); my $newlname = $query->param('newlname'); my $newimage = $query->param('newimage'); my $newusemail = $query->param('newusemail'); my $newsla = $query->param('newsla'); my $newro = $query->param('newro'); my $pwd = $query->param('pwd'); my $error; my %k2T = ( passwd => 'Password', fname => 'Name', lname => 'Surname', email => 'Email' ); if ($query->param('do')) { my $maxserial = 10; for (my $i = 0; $query->param("u$i"."login") ne undef; ++$i) { my $uid = $query->param("u$i"."login"); my $user = TTXUser->new($uid); if ($user ne undef) { $maxserial = $user->{snum} if $user->{snum} > $maxserial; } } for (my $i = 0; $query->param("u$i"."login") ne undef; ++$i) { my $uid = $query->param("u$i"."login"); my $user = TTXUser->new($uid); if ($user ne undef) { if ($query->param("u$i"."del")) { $user->delete(); my @list = grep(/^group\d+$/, $cfg->vars()); my $savecfg; foreach my $gid (@list) { my $mbrlist = $cfg->get("mbr-$gid"); if ($mbrlist =~ /\b$uid\b/) { $mbrlist =~ s/\b$uid\b//g; $mbrlist =~ s/;;/;/g; $mbrlist =~ s/^;//; $cfg->set("mbr-$gid", $mbrlist); $savecfg = 1; } } $cfg->save() if $savecfg; next; } my $dosave = 0; foreach my $key ('image', 'usemail', 'ro', 'sla') { $user->{$key} = $query->param("u$i$key"); } foreach my $key ('passwd', 'fname', 'lname', 'email') { if ($query->param("u$i$key") eq undef) { $error .= "Missing $k2T{$key} for ".$query->param("u$i"."login")."
\n"; next; } $user->{$key} = $query->param("u$i$key"); if (!$user->{snum}) { $maxserial += 1 + int(rand(10)); $user->{snum} = $maxserial; } $dosave++; } $user->save() if $dosave > 3; } } if ($newlogin ne undef) { my $user = TTXUser->new($newlogin); if ($user->{passwd} ne undef) { $error .= "User [$newlogin] already exists
\n"; } else { my $addnew = 1; foreach my $key ('image', 'usemail', 'ro', 'sla') { $user->{$key} = $query->param("new$key"); } if ($newlogin =~ /\s/) { $addnew = 0; $error .= "User Login may not contain spaces
\n"; } foreach my $key ('passwd', 'email') { if ($query->param("new$key") eq undef || $query->param("new$key") =~ /[^a-zA-Z0-9\@_=.-]/) { $addnew = 0; $error .= "Invalid or missing $k2T{$key} in the new user record
\n"; } else { $user->{$key} = $query->param("new$key"); } } foreach my $key ('fname', 'lname') { if ($query->param("new$key") eq undef) { $addnew = 0; $error .= "Missing $k2T{$key} in the new user record
\n"; } else { $user->{$key} = $query->param("new$key"); } } if ($addnew) { $maxserial += 1 + int(rand(10)); $user->{snum} = $maxserial; $user->{login} = $newlogin; if (!$user->save()) { $error .= $user->errortext(); } else { $newlogin = undef; $newpasswd = undef; $newemail = undef; $newfname = undef; $newlname=undef; $newimage=undef; $newusemail = undef; $newsla=undef; } } } } } print <System Setup | Users | Groups | Inventory

Operator Management

$error
EOT if ($cfg->get('opersla')) { print <SLA
(hours) EOT } print < EOT my @users = TTXUser::list(); my $i = 0; foreach (@users) { my $user = TTXUser->new($_); next if $user eq undef || $user->{passwd} eq undef; print "
\n". "\n". "\n". "\n". "\n". "\n". "\n". "\n". "\n". ($cfg->get('opersla') ? "\n":''). "\n"; ++$i; } my $loginlink; if ($i) { $loginlink = "
Login to Trouble Ticket Express
\n"; } else { $loginlink = "
You must create at least one operator record
\n"; } $loginlink .= "
Note: An operator with login admin is considered as a superuser
\n". "and may be allowed to execute certain privileged commands.\n"; print < EOT my $colspan = 9; if ($cfg->get('opersla')) { ++$colspan; print < EOT } print <
Del LoginPasswordEmail NameSurnameImage URL (optional) Accept Email* Read Only**
".$user->{login}."{passwd}."\">{email}."\">{fname}."\">{lname}."\">{image}."\">{usemail}? ' checked':'').">{ro}? ' checked':'').">{sla}."\">
 
* - Accept Email. If enabled the operator may submit responses using email. The feature requires Mail Module
** - Read Only. If checked the operator may not modify existing tickets.

$loginlink
EOT } # ======================================================================== grpid sub grpid { my @list = grep(/^group\d+$/, $cfg->vars()); foreach my $key (@list) { if ($cfg->get($key) eq $_[0]) { $key =~ s/^\D+//; return $key; } } return undef; } # ======================================================================== newid sub newid { my @list = grep(/^group\d+$/, $cfg->vars()); my $id; my @ids; foreach my $key (@list) { my $n = $key; $n =~ s/^\D+//; if ($cfg->get($key) eq '') { return $n; } else { $ids[$n] = 1; } } for ($id = 1; $ids[$id]; ++$id){}; return $id; } # ========================================================================= grps sub grps { $cfg = $_[0]; $query = $_[1]; eval "use TTXGroups"; my $grpmodule = 1 if !$@; my $disabled = 'disabled=disabled' if !$grpmodule; my $newgrp = $query->param('newgrp'); $newgrp =~ s/^\s+//; $newgrp =~ s/\s+$//; $newgrp =~ s/param('pwd'); my $error; my $grpsellbl = $cfg->get('grpsellbl'); my @users = TTXUser::list(); if ($query->param('do')) { my $dosave = 0; my @list = grep(/^group\d+$/, $cfg->vars()); foreach my $grid (@list) { my $grp = $cfg->get($query->param($grid)); my $i = $grid; $i =~ s/^\D+//; if ($query->param("g$i"."del")) { $cfg->set($grid, ''); $cfg->set("mbr-$grid", ''); $cfg->set('defaultgrp', '') if $cfg->get('defaultgrp') eq $grid; $dosave = 1; next; } my $name = $query->param("g$i".'name'); $name =~ s/^\s+//; $name =~ s/\s+$//; if ($name eq undef) { $error .= "Missing Group name for Group $i
\n"; next; } elsif ($grp ne $name) { $cfg->set($grid, $name); $dosave = 1; } } if ($newgrp ne undef) { if (grpid($newgrp) ne undef) { $error .= "Group [$newgrp] already exists
\n"; } else { $cfg->set("group".newid(), $newgrp); $newgrp = undef; $dosave = 1; } } foreach my $uid (@users) { my @glist = grep(/^group\d+$/, $cfg->vars()); foreach my $gid (@glist) { next if $cfg->get($gid) eq '' || $cfg->get($gid) eq undef; # skip deleted groups my $mbrlist = $cfg->get("mbr-$gid"); if ($mbrlist =~ /\b$uid\b/) { if (!$query->param("mbr-$uid-$gid")) { $mbrlist =~ s/\b$uid\b//g; $mbrlist =~ s/;;/;/g; $mbrlist =~ s/^;//; $cfg->set("mbr-$gid", $mbrlist); $dosave = 1; } } else { if ($query->param("mbr-$uid-$gid")) { $mbrlist .= ';' if $mbrlist ne undef; $mbrlist .= $uid; $cfg->set("mbr-$gid", $mbrlist); $dosave = 1; } } } } if ($query->param('usegrpsel') && !$cfg->get('usegrpsel') && $grpmodule) { $cfg->set('usegrpsel', 1); $dosave = 1; } elsif ($cfg->get('usegrpsel') && (!$query->param('usegrpsel') || !$grpmodule)) { $cfg->set('usegrpsel', 0); $dosave = 1; } if ($query->param('accessctrl') && !$cfg->get('accessctrl') && $grpmodule) { $cfg->set('accessctrl', 1); $dosave = 1; } elsif ($cfg->get('accessctrl') && (!$query->param('accessctrl') || !$grpmodule)) { $cfg->set('accessctrl', 0); $dosave = 1; } TTXCommon::cleanit($query, 'grpsellbl'); if ($query->param('grpsellbl') ne $cfg->get('grpsellbl')) { $cfg->set('grpsellbl', $query->param('grpsellbl')); $dosave = 1; } TTXCommon::cleanit($query, 'defaultgrp'); if ($query->param('defaultgrp') ne $cfg->get('defaultgrp')) { $cfg->set('defaultgrp', $query->param('defaultgrp')); $dosave = 1; } $cfg->save() if $dosave; } my $checkgrpsel = ' checked' if $cfg->get('usegrpsel'); my $checkaccessctrl = ' checked' if $cfg->get('accessctrl'); $grpsellbl = $cfg->get('grpsellbl'); $grpsellbl =~ s/"/"/g; $grpsellbl = 'Department' if $grpsellbl eq undef; print <System Setup | Users | Groups | Inventory

Group Management

$error
The Trouble Ticket Express allows assigning an operator to one or more groups. You may define an unlimited number of groups to put together operators, who are responsible for similar tasks. The division may be department based (e.g. Billing, Sales, Customer Service), product centric (Shared hosting, Dedicated Server, Collocation), skill related (Tier1, Tier2, Supervisor).

Enable Group Mode If checked, the Trouble Ticket Express will place a group selector drop down menu on a standard ticket submission form. This will ensure that each new ticket is being designated to appropriate group and only members of that group will receive new ticket notifications. Furthermore, operators will be provided with an option to transfer a ticket to a group, rather than to a particular employee. EOT if (!$grpmodule) { print <
Note: Group support requires Groups Module. The module is not installed on your server. Click here for ordering and installation instructions.

EOT } print <
Access control If checked, the Trouble Ticket Express will restrict access to tickets: an operator will only be allowed to access tickets of the group he or she is a member of.
Group selector label e.g. Department, Product, Category, Skill

EOT my @grplist = sort grep(/^group\d+$/, $cfg->vars()); @grplist = sort {$cfg->get($a) cmp $cfg->get($b)} @grplist if $cfg->get('groupalphasort'); foreach my $id (@grplist) { my $grpname = $cfg->get($id); next if $grpname eq undef || $grpname eq ''; my $i = $id; $i =~ s/^\D+//; my $checkradio = ' checked' if $cfg->get('defaultgrp') eq $id; print "\n". "\n". "\n". "\n". "\n"; } print <
Del Group ID Group Name Default*
Group $i
   
*All tickets submitted via email will be assigned the default group id.
Email based submissions require optional Email Module.


Membership Matrix
EOT my $colcnt = 1; foreach my $id (sort @grplist) { my $grpname = $cfg->get($id); next if $grpname eq undef || $grpname eq ''; print "\n"; ++$colcnt; } print "\n"; foreach my $login (sort @users) { my $user = TTXUser->new($login); next if $user eq undef || $user->{passwd} eq undef; print "\n"; foreach my $id (sort @grplist) { my $grpname = $cfg->get($id); next if $grpname eq undef || $grpname eq ''; my $checked = ' checked' if $cfg->get("mbr-$id") =~ /\b$login\b/; print "\n"; } print "\n"; } print <
Operator$grpname
".$user->{fname}.' '.$user->{lname}." ($login)
EOT } # ==================================================================== tmplreset sub tmplreset { my ($cfg, $tmplname) = @_; my $basedir = $cfg->get('basedir'); $basedir =~ /(.*)/; $basedir = $1; if ($tmplname ne undef) { mkdir("$basedir/templates", 0777) if ! -d "$basedir/templates"; foreach my $tmpl (@templates) { next if $tmpl->{name} ne $tmplname; if (open(TMPL, ">$basedir/templates/".$tmpl->{name})) { print TMPL $tmpl->{data}; close TMPL; chmod(0777, "$basedir/templates/".$tmpl->{name}); return undef; } } return "Unknown template $tmplname"; } return undef; } # ======================================================================== build sub build { my $cfg = $_[0]; umask(0); my $basedir = $cfg->get('basedir'); $basedir =~ /(.*)/; $basedir = $1; foreach my $d ('templates', 'tickets', 'sid', 'tmp') { mkdir("$basedir/$d", 0777) if ! -d "$basedir/$d"; } foreach my $tmpl (@templates) { if (open(TMPL, ">$basedir/templates/".$tmpl->{name})) { print TMPL $tmpl->{data}; close TMPL; chmod(0777, "$basedir/templates/".$tmpl->{name}); } } } # ==================================================================== scriptdir sub scriptdir { my $dir = $ENV{'SCRIPT_FILENAME'}; $dir = $ENV{'PATH_TRANSLATED'} if $ENV{'PATH_TRANSLATED'} ne undef; $dir = $ENV{'pathTranslated'} if $ENV{'pathTranslated'} ne undef; return $ENV{'PWD'} if $dir eq undef; my @path = split(/\/|\\/, $dir); pop(@path); $dir = join("\/", @path); return $dir; } # ================================================================= listsendmail sub listsendmail { my @list; if ($^O ne 'MSWin32') { my @options = ('/usr/bin/sendmail', '/usr/sbin/sendmail', '/usr/lib/sendmail'); foreach my $option (@options) { if (-x $option) { push @list, $option; } } } return @list; } # ================================================================ whichsendmail sub whichsendmail { if ($^O ne 'MSWin32') { my @options = ('/usr/bin/sendmail', '/usr/sbin/sendmail', '/usr/lib/sendmail'); foreach my $option (@options) { if (-f $option) { print "$option
\n"; } } } } # ====================================================================== sysinfo sub sysinfo { $cfg = $_[0]; $query = $_[1]; my $pwd = $query->param('pwd'); print < EOT print "
OS: $^O

\n"; print "Perl: $]

\n"; whichsendmail(); eval "use DBI"; if ($@ ne undef) { print "No DBI module found.
\n"; } else { my $dbiversion; eval "$DBI::VERSION;"; if ($@ eq undef) { $dbiversion = $DBI::VERSION; } print "DBI module "; if ($dbiversion ne undef) { print " version $dbiversion "; } else { print " of unknown version "; } print "found
\n"; my @drivers = DBI->available_drivers(1); if (@drivers > 0) { print "
DBD Drivers found:
\n"; foreach my $driver (@drivers) { print "$driver "; if ($driver eq 'mysql') { print eval 'use DBD::mysql; $DBD::mysql::VERSION;'; } print "
\n"; } } } eval "use Net::SMTP"; if ($@ ne undef) { print "No Net::SMTP module found.
\n"; } else { eval "$Net::SMTP::VERSION;"; if ($@ eq undef) { print 'Net:'.":SMTP module version $Net::SMTP::VERSION found
\n"; } } eval "use Authen::SASL"; if ($@ ne undef) { print "No Authen::SASL module found.
\n"; } else { eval "$Authen::SASL::VERSION;"; if ($@ eq undef) { print 'Authen:'.":SASL module version $Authen::SASL::VERSION found
\n"; } } eval "use MIME::Parser"; if ($@ ne undef) { print "No MIME::Parser module found.
\n"; } else { eval "$MIME::Parser::VERSION;"; if ($@ eq undef) { print 'MIME:'.":Parser module version $MIME::Parser::VERSION found
\n"; } } eval "use Time::Local"; if ($@ ne undef) { print "No Time::Local module found.
\n"; } else { eval "$Time::Local::VERSION;"; if ($@ eq undef) { print 'Time:'.":Local module version $Time::Local::VERSION found
\n"; } else { print 'Time:'.":Local module unknown version found
\n";} } eval "use GD"; if ($@ ne undef) { print "No GD module found.
\n"; } else { eval "$GD::VERSION;"; if ($@ eq undef) { print "GD module version $GD::VERSION found
\n"; } } my $dir = scriptdir(); if (-d $dir && opendir(CGIDIR, $dir)) { my @files = grep { /\.pm$/ && -f "$dir/$_" } readdir(CGIDIR); closedir CGIDIR; print < EOT my %minver = ( TTXAnswerLib => 2.19, TTXFile => 2.19, TTXGroups => 2.19, TTXLayout => 2.21 ); foreach my $f (sort @files) { my $m = $f; $m =~ s/\.pm$//; next if $m !~ /^TTX/; print "\n"; eval "use $m"; if ($@ ne undef) { print "\n"; } else { print "\n\n"; } print "\n"; } } print "
Trouble Ticket Express modules available
Module Version Revision
$m Error: $@"; my $v; eval "\$v= \$$m:".":VERSION"; if ($@ eq undef && $v ne undef) { if ($v < $TTXSetup::VERSION && ($minver{$m} eq undef || $minver{$m} > $v)) { print "$v, please upgrade" } else { print "$v"; } } else { print "-"; } print ""; eval "\$v= \$$m:".":REVISION"; if ($@ eq undef && $v ne undef) { print "$v"; } else { print '-' } print "

\n"; print < EOT } # =================================================================== invenabled my $_invenabled = undef; sub invenabled { if ($_invenabled eq undef) { eval 'use TTXInvMod'; if ($@ eq undef) { $_invenabled = 1; } else { $_invenabled = 0; } } return $_invenabled; } # ==================================================================== inventory sub inventory { $cfg = $_[0]; $query = $_[1]; my $pwd = $query->param('pwd'); my $noinvmod; if (!invenabled()) { $noinvmod = <Note: The Inventory Module is not installed on your server. Although you may manage your inventory, the inventory functionality will not be available through user interface. For ordering information please refer to the Inventory Module Page.

EOT } print <System Setup | Users | Groups | Inventory

Inventory Tracking

$noinvmod The Inventory Tracking subsystem allows defining a list of items such as servers, workstations, projects, premises, cars - anything you need to develop or provide a service for.

To track status of an item you may use notes and status indicator. The note is a plain next (markup available) attached to an item. An operator may append messages to the notes and you may edit notes using administrator interface.

You may assign any predefined state to the status field. You may define as many status values (such as OK, Maintenance, Repair, Occupied etc) as you need.

You may associate an image with each state and item.
EOT eval "use TTXInventory"; if ($@ ne undef) { print "

No TTXInventory.pm module available\n

Error: $@
\n"; return; } my %inputs; my %inputs_quot; my %inputs_lt; my @inputlist = ('dbrdtitle', 'label', 'show'); foreach my $key (@inputlist) { if ($query->param('do') ne 'gen') { $inputs{$key} = $cfg->get("inventory.$key"); } else { $inputs{$key} = TTXCommon::cleanit($query, $key); } $inputs_quot{$key} = $inputs{$key}; $inputs_quot{$key} =~ s/"/"/g; $inputs_lt{$key} = $inputs{$key}; $inputs_lt{$key} =~ s/param('do') eq 'gen') { foreach my $key ('dbrdtitle', 'label', 'show') { $cfg->set("inventory.$key", $inputs{$key}); } $cfg->save(); } # # Items =========================================== # my $newtitle = TTXCommon::cleanit($query, 'newtitle'); my $newimg = TTXCommon::cleanit($query, 'newimg'); my $newnotes = TTXCommon::cleanit($query, 'newnotes'); if ($query->param('do') eq 'items') { my $newid; TTXInventory::lockdb(); if ($newtitle ne undef) { my %item; $item{title} = $newtitle; $item{img} = $newimg; $item{notes} = $newnotes; $newid = TTXInventory::add(\%item); if ($newid eq undef) { my $err = TTXInventory::error(); print < Error: $err EOT } else { $newtitle = $newimg = $newnotes = undef; } } # # Process item deletions & updates # my @ilist = sort {$a <=> $b}TTXInventory::list(); my $dflt = $newid; foreach my $id (@ilist) { next if $id eq $newid; if ($query->param("del$id")) { TTXInventory::del($id); next; } my $item = TTXInventory::getitem($id); next if $item eq undef; $dflt = $id if ($query->param('dflt') eq $id || $dflt eq undef); $item->{title} = TTXCommon::cleanit($query, "title$id"); $item->{img} = TTXCommon::cleanit($query, "img$id"); $item->{notes} = TTXCommon::cleanit($query, "notes$id"); } TTXInventory::savedb(); TTXInventory::unlockdb(); if ($dflt ne $cfg->get('inventory.defaultitem')) { $cfg->set('inventory.defaultitem', $dflt); $cfg->save(); } } # # Status =========================================== # my $newstitle = TTXCommon::cleanit($query, 'newstitle'); my $newsimg = TTXCommon::cleanit($query, 'newsimg'); if ($query->param('do') eq 'status') { my $newid; TTXInventory::lockdb(); my $states = TTXInventory::loadstates(); if ($newstitle ne undef) { my %state; $state{title} = $newstitle; $state{img} = $newsimg; $newid = TTXInventory::addstate(\%state); if ($newid eq undef) { my $err = TTXInventory::error(); print < Error: $err EOT } else { $newstitle = $newsimg = undef; } } # # Process state deletions & updates # my @slist = sort {$a <=> $b} keys %{$states}; my $dflt = $newid; foreach my $id (@slist) { next if $id eq $newid; if ($query->param("del$id")) { delete $states->{$id}; next; } my $state = $states->{$id}; next if $state eq undef; $dflt = $id if ($query->param('dflt') eq $id || $dflt eq undef); $state->{title} = TTXCommon::cleanit($query, "title$id"); $state->{img} = TTXCommon::cleanit($query, "img$id"); } TTXInventory::savestates(); TTXInventory::unlockdb(); if ($dflt ne $cfg->get('inventory.defaultstate')) { $cfg->set('inventory.defaultstate', $dflt); $cfg->save(); } } my $showsel = ' checked' if $cfg->get('inventory.show'); print <

General Settings
DashBoard Title The DashBoard page provides a review of all inventory items.
Item Label E.g. System, Server, Project etc.
Show Selector Show item selector on contact form
EOT my $label_lt = $inputs_lt{label}; $label_lt = 'Item' if $label_lt eq undef; my $label_quot = $inputs_quot{label}; $label_quot = 'Item' if $label_quot eq undef; print <
EOT my @ilist = sort {$a <=> $b}TTXInventory::list(); foreach my $id (@ilist) { my $item = TTXInventory::getitem($id); next if $item eq undef; my $title = $item->{title}; $title =~ s/"/"/g; my $img = $item->{img}; $img =~ s/"/"/g; my $simg = $img || $cfg->get('imgurl').'/dot.gif'; my $isdefault = ' checked' if $cfg->get('inventory.defaultitem') eq $id; my $notes = $item->{notes}; $notes =~ s/ EOT } my $newtitle_quot = $newtitle; $newtitle_quot =~ s/"/%quot;/g; my $newimg_quot = $newimg; $newimg_quot =~ s/"/%quot;/g; my $newnotes_lt = $newnotes; $newnotes_lt =~ s/"/%quot;/g; print <

$label_lt List
Del Id Title Default Image URL
$id
  Notes

New  
  Notes
EOT print <
EOT my $states = TTXInventory::loadstates(); if ($states ne undef) { my @slist = sort {$a <=> $b} keys %{$states}; foreach my $id (@slist) { my $state = $states->{$id}; next if $state eq undef; my $title = $state->{title}; $title =~ s/"/"/g; my $img = $state->{img}; $img =~ s/"/"/g; my $simg = $img || $cfg->get('imgurl').'/dot.gif'; my $isdefault = ' checked' if $cfg->get('inventory.defaultstate') eq $id; print < EOT } } else { my $err = TTXInventory::error(); print < EOT } my $newstitle_quot = $newstitle; $newstitle_quot =~ s/"/%quot;/g; my $newsimg_quot = $newsimg; $newsimg_quot =~ s/"/%quot;/g; print <

States
Del Id Title Default Image URL
$id
Error: $err
New  
EOT } 1; =cut