base
This commit is contained in:
@@ -0,0 +1,392 @@
|
||||
package TTXFileTickets;
|
||||
#
|
||||
# This module is a part of Trouble Ticket Express package
|
||||
# http://www.troubleticketexpress.com
|
||||
#
|
||||
# COPYRIGHT: 2002-2005, United Web Coders
|
||||
# http://www.unitedwebcoders.com
|
||||
#
|
||||
# $Revision: 290 $
|
||||
# $Date: 2006-10-19 14:09:29 +0400 (Thu, 19 Oct 2006) $
|
||||
#
|
||||
|
||||
$TTXFileTickets::VERSION='2.22';
|
||||
BEGIN {
|
||||
$TTXFileTickets::REVISION = '$Revision: 290 $';
|
||||
if ($TTXFileTickets::REVISION =~ /(\d+)/) {
|
||||
$TTXFileTickets::REVISION = $1;
|
||||
}
|
||||
};
|
||||
use strict;
|
||||
use vars qw(@ISA);
|
||||
require TTXTickets;
|
||||
@ISA = qw(TTXTickets);
|
||||
use TTXData;
|
||||
require TTXSearch;
|
||||
require TTXMarkup;
|
||||
|
||||
my %errmsgs = (
|
||||
NOUSER => 'User does not exist',
|
||||
OPENERR => 'Error reading tickets database',
|
||||
WRERR => 'Error writing file',
|
||||
LOCKWRERR => 'Error acquiring lock (failed to create lock file)',
|
||||
LOCKOPNERR => 'Error acquiring lock (failed to open lock file)',
|
||||
LOCKERR => 'Error acquiring lock (flock() failed on lock file)',
|
||||
);
|
||||
|
||||
my $boundary = '-----------asdjhfKJS12869nmboiu7826318---';
|
||||
|
||||
# ========================================================================== new
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $self = $class->SUPER::new(@_);
|
||||
$self->{_LOCK_CNT} = 0;
|
||||
$self->load();
|
||||
return $self;
|
||||
}
|
||||
# ====================================================================== DESTROY
|
||||
|
||||
sub DESTROY {
|
||||
my $self = shift;
|
||||
if ($self->{_LOCK_CNT}) {
|
||||
$self->{_LOCK_CNT} = 1;
|
||||
$self->unlockdb();
|
||||
}
|
||||
}
|
||||
# ========================================================================= load
|
||||
|
||||
sub load {
|
||||
my $self = shift;
|
||||
return 0 if !$self->lockdb();
|
||||
my $fn = TTXData::get('CONFIG')->get('ticketdb');
|
||||
return 1 if ! -f $fn;
|
||||
if (!open(DB, $fn)) {
|
||||
$self->{_ERROR_CODE} = 'OPENERR';
|
||||
$self->unlockdb();
|
||||
return 0;
|
||||
}
|
||||
my @buff = <DB>;
|
||||
close DB;
|
||||
chomp @buff;
|
||||
foreach my $line (@buff) {
|
||||
my @fields = split(/\|/, $line);
|
||||
my $tik;
|
||||
foreach my $fld ($self->_fields()) {
|
||||
$tik->{$fld} = shift @fields;
|
||||
}
|
||||
$self->{TICKETS}->{$tik->{id}} = $tik;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
# ======================================================================= lockdb
|
||||
|
||||
sub lockdb {
|
||||
my $self = shift;
|
||||
if ($self->{_LOCK_CNT}) {
|
||||
++$self->{_LOCK_CNT};
|
||||
return 1;
|
||||
}
|
||||
my $lockfile = TTXData::get('CONFIG')->get('basedir')."/lockdb.ttx";
|
||||
if (! -f $lockfile) {
|
||||
if (!open(LOCK, ">$lockfile")) {
|
||||
$self->{_ERROR_CODE} = 'LOCKWRERR';
|
||||
return 0;
|
||||
}
|
||||
} elsif (!open(LOCK,"+<$lockfile")) {
|
||||
$self->{_ERROR_CODE} = 'LOCKOPNERR';
|
||||
return 0;
|
||||
}
|
||||
flock(LOCK, 2);
|
||||
$self->{_LOCK_CNT} = 1;
|
||||
return 1;
|
||||
}
|
||||
# ===================================================================== unlockdb
|
||||
|
||||
sub unlockdb {
|
||||
my $self = shift;
|
||||
return if !$self->{_LOCK_CNT};
|
||||
--$self->{_LOCK_CNT};
|
||||
if (!$self->{_LOCK_CNT}) {
|
||||
flock(LOCK, 8);
|
||||
close LOCK;
|
||||
}
|
||||
}
|
||||
# ========================================================================= save
|
||||
|
||||
sub save {
|
||||
my $self = shift;
|
||||
return 0 if !$self->lockdb();
|
||||
#
|
||||
# Build temporary file
|
||||
#
|
||||
my $tmpfn = TTXData::get('CONFIG')->get('ticketdb').'.tmp';
|
||||
if (!open(TMPDB, ">$tmpfn")) {
|
||||
$self->{_ERROR_CODE} = 'WRERR';
|
||||
$self->unlockdb();
|
||||
return 0;
|
||||
}
|
||||
flock(TMPDB, 2);
|
||||
foreach my $id (keys %{$self->{TICKETS}}) {
|
||||
my $line;
|
||||
my $tik = $self->{TICKETS}->{$id};
|
||||
foreach my $fld ($self->_fields()) {
|
||||
my $val = $tik->{$fld};
|
||||
$val =~ s/\|/!/g;
|
||||
$val =~ s/\r//g;
|
||||
$val =~ s/\n/ /g;
|
||||
$line .= "$val|";
|
||||
}
|
||||
print TMPDB "$line\n";
|
||||
if ($tik->{messages} ne undef) {
|
||||
my $fn = TTXData::get('CONFIG')->get('basedir')."/tickets/$id.cgi";
|
||||
if (open(MSG, ">$fn")) {
|
||||
flock(MSG, 2);
|
||||
foreach my $msg (@{$tik->{messages}}) {
|
||||
print MSG "$msg\n$boundary\n";
|
||||
}
|
||||
truncate(MSG, tell(MSG));
|
||||
close MSG;
|
||||
umask(0);
|
||||
chmod(0777, $fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
close(TMPDB);
|
||||
umask(0);
|
||||
chmod(0777, $tmpfn);
|
||||
#
|
||||
# Check if the ticket database exists, create if none.
|
||||
#
|
||||
my $fn = TTXData::get('CONFIG')->get('ticketdb');
|
||||
if (! -f $fn) {
|
||||
if (!open(DB, ">$fn")) {
|
||||
$self->{_ERROR_CODE} = 'WRERR';
|
||||
$self->unlockdb();
|
||||
return 0;
|
||||
}
|
||||
close DB;
|
||||
}
|
||||
#
|
||||
# Check if it's time for backup
|
||||
#
|
||||
my $backupfq = TTXData::get('CONFIG')->get('backupfq');
|
||||
$backupfq = 7 * 24 * 60 * 60; # default is weekly
|
||||
my $tmext = time();
|
||||
$tmext = $tmext - ($tmext % $backupfq);
|
||||
my $backupdir = TTXData::get('CONFIG')->get('basedir').'/backup';
|
||||
my $backupfn = $backupdir."/tickets$tmext.cgi";
|
||||
if (! -f $backupfn) {
|
||||
if (! -d $backupdir) {
|
||||
umask(0);
|
||||
if (!mkdir($backupdir, 0777)) {
|
||||
warn "Failed to create directory '$backupdir'";
|
||||
}
|
||||
}
|
||||
if (-d $backupdir) {
|
||||
rename $fn, $backupfn;
|
||||
}
|
||||
}
|
||||
#
|
||||
# Replace ticket DB
|
||||
#
|
||||
if (!rename($tmpfn,$fn)) {
|
||||
umask 0;
|
||||
chmod(0777, $fn);
|
||||
if (!rename($tmpfn,$fn)) {
|
||||
warn "Failed to 'rename($tmpfn,$fn), make sure the $fn is NOT read-only.";
|
||||
$self->{_ERROR_CODE} = 'WRERR';
|
||||
$self->unlockdb();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
$self->unlockdb();
|
||||
if (TTXData::get('_USECACHE')) {
|
||||
eval "use TTXCache";
|
||||
TTXCache::purge() if $@ eq undef;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
# ======================================================================= deltik
|
||||
|
||||
sub deltik {
|
||||
my $self = shift;
|
||||
my $id = shift;
|
||||
my $fn = TTXData::get('CONFIG')->get('basedir')."/tickets/$id.cgi";
|
||||
$fn =~ /(.*)/; $fn = $1;
|
||||
unlink $fn if -f $fn;
|
||||
if (TTXData::get('ISPRO')) {
|
||||
TTXFile::delfiles($id);
|
||||
}
|
||||
delete $self->{TICKETS}->{$id};
|
||||
}
|
||||
# ================================================================== ticketbykey
|
||||
|
||||
sub ticketbykey {
|
||||
my $self = shift;
|
||||
my $key = shift;
|
||||
my $id = $key;
|
||||
$id =~ s/Z.*$//;
|
||||
if (TTXData::get('CONFIG')->get('shortkey') || $self->{TICKETS}->{$id}->{key} eq $key) {
|
||||
return $self->ticket($id);
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
# ======================================================================= ticket
|
||||
|
||||
sub ticket {
|
||||
my ($self, $id, $nomsg) = @_;
|
||||
my $tik = $self->{TICKETS}->{$id};
|
||||
if ($tik ne undef && $tik->{message} eq undef && !$nomsg) {
|
||||
my $fn = TTXData::get('CONFIG')->get('basedir')."/tickets/$id.cgi";
|
||||
if (-f $fn && open(MSG, $fn)) {
|
||||
my $rawmsg;
|
||||
read(MSG, $rawmsg, 64*1024);
|
||||
close MSG;
|
||||
my @messages = split(/\n$boundary\n/, $rawmsg);
|
||||
$tik->{messages} = \@messages;
|
||||
}
|
||||
}
|
||||
return $tik;
|
||||
}
|
||||
# ==================================================================== addticket
|
||||
|
||||
sub addticket {
|
||||
my $self = shift;
|
||||
my $tik = shift;
|
||||
my $fn = TTXData::get('CONFIG')->get('ticketid');
|
||||
if (! -f $fn) {
|
||||
if (!open(ID, ">$fn")) {
|
||||
$self->{_ERROR_CODE} = 'WRERR';
|
||||
return 0;
|
||||
}
|
||||
print ID "0\n";
|
||||
close ID;
|
||||
}
|
||||
if (!open(ID,"+<$fn")) {
|
||||
$self->{_ERROR_CODE} = 'WRERR';
|
||||
return 0;
|
||||
}
|
||||
flock(ID, 2);
|
||||
my $id = <ID>;
|
||||
chomp $id;
|
||||
++$id;
|
||||
seek(ID, 0, 0);
|
||||
print ID "$id\n";
|
||||
close ID;
|
||||
umask(0);
|
||||
chmod(0777, $fn);
|
||||
$tik->{open} = time() if $tik->{open} eq undef;
|
||||
$tik->{updated} = $tik->{open};
|
||||
$tik->{status} = 'PND';
|
||||
$tik->{id} = $id;
|
||||
$tik->{key} = "$id";
|
||||
if (!TTXData::get('CONFIG')->get('shortkey')) {
|
||||
$tik->{key} .= "Z$$".int(1000000 + rand(9000000)).int(1000000 + rand(9000000));
|
||||
}
|
||||
$self->{TICKETS}->{$id} = $tik;
|
||||
return $id;
|
||||
}
|
||||
# ========================================================================= list
|
||||
|
||||
sub list {
|
||||
my ($self, $off, $win, $col, $ord, $filter, $drange) = @_;
|
||||
my @list = keys %{$self->{TICKETS}};
|
||||
my $tickets = $self->{TICKETS};
|
||||
my $byid;
|
||||
my $ftxt;
|
||||
my $abstract = {};
|
||||
if ($filter ne undef) {
|
||||
foreach my $f (@{$filter}) {
|
||||
my $expr = $f->{expr};
|
||||
my $c = $f->{col};
|
||||
if ($c eq 'text') {
|
||||
$ftxt = $f;
|
||||
} elsif ($c eq '-grp-') {
|
||||
my $oper = $f->{oper};
|
||||
my $grps = join('|', @{$f->{groups}});
|
||||
if ($grps ne undef) {
|
||||
@list = grep {$tickets->{$_}->{oper} eq $oper || $tickets->{$_}->{grp} =~ /^$grps$/} @list;
|
||||
} else {
|
||||
@list = grep {$tickets->{$_}->{oper} eq $oper} @list;
|
||||
}
|
||||
} else {
|
||||
$byid = 1 if $c eq 'id';
|
||||
@list = grep {$tickets->{$_}->{$c} =~ /$expr/i} @list;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$byid && TTXData::get('CONFIG')->get('hideoldsolved')) {
|
||||
my $oldis = int(TTXData::get('CONFIG')->get('hideoldsolved'));
|
||||
$oldis = time() - $oldis * 3600 * 24;
|
||||
@list = grep {$tickets->{$_}->{status} ne 'CLS' || $tickets->{$_}->{closed} > $oldis} @list;
|
||||
}
|
||||
if ($drange->{fld} ne undef) {
|
||||
my %fldmap = ( created => 'open', updated => 'updated', solved => 'closed' );
|
||||
my $fld = $fldmap{$drange->{fld}};
|
||||
my $stm = $drange->{stm};
|
||||
my $etm = $drange->{etm};
|
||||
@list = grep {$tickets->{$_}->{$fld} >= $stm && $tickets->{$_}->{$fld} <= $etm} @list;
|
||||
}
|
||||
if ($ftxt ne undef) {
|
||||
my @l1;
|
||||
my $tokens = TTXSearch::tokenize($ftxt->{expr});
|
||||
my $match;
|
||||
my $body;
|
||||
my $token;
|
||||
foreach my $id (@list) {
|
||||
my $t = $self->ticket($id);
|
||||
next if $t eq undef;
|
||||
foreach my $msg (@{$t->{messages}}) {
|
||||
$msg =~ s/^.+\n\n//s;
|
||||
$body = TTXMarkup::strip($msg);
|
||||
$match = 1;
|
||||
foreach $token (@{$tokens}) {
|
||||
if ($body !~ /\b$token\b/i) {
|
||||
$match = 0;
|
||||
last;
|
||||
}
|
||||
}
|
||||
if ($match) {
|
||||
$abstract->{$id} = TTXSearch::abstract($body, $tokens);
|
||||
push @l1, $id;
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
@list = @l1;
|
||||
}
|
||||
if (grep(/^$col$/, ('open', 'closed', 'updated', 'id'))) {
|
||||
if ($ord eq 'A') {
|
||||
@list = sort {$tickets->{$a}->{$col} <=> $tickets->{$b}->{$col}} @list;
|
||||
} else {
|
||||
@list = sort {$tickets->{$b}->{$col} <=> $tickets->{$a}->{$col}} @list;
|
||||
}
|
||||
} else {
|
||||
if ($ord eq 'A') {
|
||||
@list = sort {$tickets->{$a}->{$col} cmp $tickets->{$b}->{$col}} @list;
|
||||
} else {
|
||||
@list = sort {$tickets->{$b}->{$col} cmp $tickets->{$a}->{$col}} @list;
|
||||
}
|
||||
}
|
||||
$off = 0 if $off < 0 || $off > @list;
|
||||
$win = 20 if $win < 1;
|
||||
$win = @list - $off if ($off + $win) > @list;
|
||||
my $browser;
|
||||
$browser->{abstract} = $abstract;
|
||||
$browser->{total} = int(@list);
|
||||
@{$browser->{list}} = splice @list, $off, $win;
|
||||
$browser->{first} = $off;
|
||||
$browser->{last} = $off + @{$browser->{list}};
|
||||
return $browser;
|
||||
}
|
||||
# ==================================================================== errortext
|
||||
|
||||
sub errortext {
|
||||
my $self = shift;
|
||||
return $errmsgs{$self->{_ERROR_CODE}};
|
||||
}
|
||||
|
||||
1;
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user