summaryrefslogtreecommitdiff
path: root/fml/lib
diff options
context:
space:
mode:
authorfukachan <fukachan>2001-11-03 09:59:10 +0000
committerfukachan <fukachan>2001-11-03 09:59:10 +0000
commitb0aa33f75ad6dd3e8ce238fd302c9d8d54925120 (patch)
treecbaa1fd04879d3c4eb350593982c3618ad7dff1a /fml/lib
parent04db364adfa8a678b4a6226205c1b245a3b8a163 (diff)
downloadfml8-b0aa33f75ad6dd3e8ce238fd302c9d8d54925120.tar.gz
fml8-b0aa33f75ad6dd3e8ce238fd302c9d8d54925120.tar.bz2
fml8-b0aa33f75ad6dd3e8ce238fd302c9d8d54925120.zip
clean up and moved to Mail::ThreadTrack
Diffstat (limited to 'fml/lib')
-rw-r--r--fml/lib/FML/Ticket/Model/minimal_states.pm1133
-rw-r--r--fml/lib/FML/Ticket/System.pm164
2 files changed, 0 insertions, 1297 deletions
diff --git a/fml/lib/FML/Ticket/Model/minimal_states.pm b/fml/lib/FML/Ticket/Model/minimal_states.pm
deleted file mode 100644
index f6cc96c9..00000000
--- a/fml/lib/FML/Ticket/Model/minimal_states.pm
+++ /dev/null
@@ -1,1133 +0,0 @@
-#-*- perl -*-
-#
-# Copyright (C) 2001 Ken'ichi Fukamachi
-# All rights reserved. This program is free software; you can
-# redistribute it and/or modify it under the same terms as Perl itself.
-#
-# $FML: minimal_states.pm,v 1.22 2001/10/08 23:26:08 fukachan Exp $
-#
-
-package FML::Ticket::Model::minimal_states;
-
-use strict;
-use vars qw(@ISA @EXPORT @EXPORT_OK);
-use Carp;
-use File::Spec;
-use FML::Log qw(Log LogWarn LogError);
-
-use FML::Ticket::System;
-@ISA = qw(FML::Ticket::System);
-
-
-=head1 NAME
-
-FML::Ticket::Model::minimal_states - a ticket system minimal_states
-
-=head1 SYNOPSIS
-
- use FML::Ticket::Model::minimal_states;
- my $ticket = FML::Ticket::Model::minimal_states->new($curproc, $args);
- if (defined $ticket) {
- $ticket->assign($curproc, $args);
- $ticket->update_status($curproc, $args);
- $ticket->update_db($curproc, $args);
- }
-
-=head1 DESCRIPTIONS
-
-This is the simplest model which implements a ticket system.
-This model can handle simple ticket C<open> and C<close>.
-
-This routine adds the ticket-id at the last of the subject in each
-article. We track the ticket-id in articles to follow the ticket
-status.
-
-=head2 CLASS HIERARCHY
-
- FML::Ticket::System
- |
- A
- -----------------
- | |
- minimal_states model2 ....
-
-If you use fmlticket.cgi, the hierarchy follows:
-
- FML::Process::Kernel
- |
- A
- FML::Process::CGI has-a FML::CGI::TicketSystem::minimal_states
-
-=head1 METHODS
-
-=head2 C<new($curproc, $args)>
-
-the constructor.
-
-=cut
-
-
-sub new
-{
- my ($self, $curproc, $args) = @_;
- my ($type) = ref($self) || $self;
- my $me = {};
- bless $me, $type;
-
- # initialize directory for DB files for further work
- my $config = $curproc->{ config };
- my $ml_name = $config->{ ml_name };
-
- croak("specify \$ml_name\n") unless defined $ml_name;
-
- $me->{ _fd } = $args->{ fd } || \*STDOUT;
- $me->{ _db_dir } = File::Spec->catfile($config->{ ticket_db_dir },
- $ml_name);
- $me->_init_ticket_db_dir($curproc, $args) || do {
- Log("fail to initialize ticket_db_dir");
- return undef;
- };
-
- # index for cross reference over mailing lists.
- $me->{ _index_db } = File::Spec->catfile($config->{ ticket_db_dir },
- "\@index");
-
- # pragma for operations hints
- $me->{ _pragma } = '';
-
- return bless $me, $type;
-}
-
-
-sub DESTROY {}
-
-
-sub mode
-{
- my ($self, $args) = @_;
- $self->{ _mode } = $args->{ mode } || 'text';
-}
-
-
-=head2 C<assign($curproc, $args)>
-
-assign a new ticket or extract the existing ticket-id from the subject.
-
-=cut
-
-
-# Descriptions: assign a new ticket or
-# extract the existing ticket-id from the subject
-# Arguments: $self $curproc $args
-# Side Effects: a new ticket_id may be assigned
-# article header is rewritten
-# Return Value: none
-sub assign
-{
- my ($self, $curproc, $args) = @_;
- my $config = $curproc->{ config };
- my $header = $curproc->{ incoming_message }->{ header };
- my $subject = $header->get('subject');
- my $pcb = $curproc->{ pcb };
-
- use FML::Header::Subject;
- my $is_reply = FML::Header::Subject->is_reply( $subject );
- my $has_ticket_id = $self->_extract_ticket_id($header, $config);
- my $pragma = $header->get('x-ticket-pragma') || '';
-
- # hmm, we cannot extract ticket_id but we try to
- # speculate ticket_id by header information.
- unless ($has_ticket_id) {
- $has_ticket_id = $self->_speculate_ticket_id($curproc, $args);
- if ($has_ticket_id) {
- $is_reply = 1;
- $self->{ _ticket_id } = $has_ticket_id;
- Log("speculated id=$has_ticket_id");
- }
- else {
- Log("(debug) fail to spelucate ticket_id");
- }
- }
-
- # If the header has a "X-Ticket-Pragma: ignore" field,
- # we ignore this mail.
- if ($pragma =~ /ignore/i) {
- $self->{ _pragma } = 'ignore';
- $self->_set_status_info("ignored");
- return undef;
- }
-
- # if the header carries "Subject: Re: ..." with ticket-id,
- # we do not rewrite the subject but save the extracted $ticket_id.
- if ($is_reply && $has_ticket_id) {
- Log("reply message with ticket_id=$has_ticket_id");
- $self->{ _ticket_id } = $has_ticket_id;
- $self->{ _status } = 'going';
- $self->_set_status_info("going");
- }
- elsif ($has_ticket_id) {
- Log("usual message with ticket_id=$has_ticket_id");
- $self->{ _ticket_id } = $has_ticket_id;
- $self->_set_status_info("found");
- }
- else {
- Log("message with no ticket_id");
-
- # assign a new ticket number for a new message
- # call SUPER class's FML::Ticket::System::increment_id()
- my $id = $self->increment_id( $config->{ ticket_sequence_file } );
-
- # O.K. rewrite Subject: of the article to distribute
- unless ($self->error) {
- my $header = $curproc->{ article }->{ header };
-
- $pcb->set('article', 'id', $id); # save $id info in PCB
- $self->_rewrite_header($header, $config, $id);
- $self->_set_status_info("newly assigned");
- }
- else {
- Log( $self->error );
- }
- }
-}
-
-
-# For example, consider a posting to both elena ML and rudo (DM) from kenken.
-#
-# From: kenken
-# To: elena-ml
-# Cc: rudo
-#
-# The reply to this DM (direct message) from rudo is
-#
-# From: rudo
-# To: elena-ml
-#
-# This reply message has no ticket_id since the message from kenken to
-# rudo comes directly from kenken not through fml driver.
-# In this case, we try to speculdate the reply relation and the ticket_id
-# of this thread by using _speculate_ticket_id().
-#
-sub _speculate_ticket_id
-{
- my ($self, $curproc, $args) = @_;
- my $header = $curproc->{ incoming_message }->{ header };
- my $midlist = $header->extract_message_id_references();
- my $result = '';
-
- for (@$midlist) { Log("(debug) mid=$_");}
-
- if (defined $midlist) {
- $self->open_db($curproc, $args);
-
- # prepare hash table tied to db_dir/*db's
- my $rh = $self->{ _hash_table };
-
- for my $mid (@$midlist) {
- $result = $rh->{ _message_id }->{ $mid };
- last if $result;
- }
-
- $self->close_db($curproc, $args);
- }
-
- Log("(debug) not speculated") unless $result;
- $result;
-}
-
-
-sub _set_status_info
-{
- my ($self, $s) = @_;
- $self->{ _status_info } .= $self->{ _status_info } ? " -> ".$s : $s;
-}
-
-
-sub update_status
-{
- my ($self, $curproc, $args) = @_;
- my $header = $curproc->{ incoming_message }->{ header };
- my $body = $curproc->{ incoming_message }->{ body };
-
- return if $self->{ _pragma } eq 'ignore';
-
- # entries to check
- my $subject = $header->get('subject');
- my $pragma = $header->get('x-ticket-pragma') || '';
-
- my $content = '';
- my $message = $body->get_first_plaintext_message();
- if ( ref($message) eq 'Mail::Message' ) {
- $content = $message->data_in_body_part();
- }
- else {
- Log("Error: get_first_plaintext_message cannot get object");
- Log("\$message = ". ref($message) );
- Log("\$message is undef()") unless defined $message;
- }
-
- if ($content =~ /^\s*close/ ||
- $subject =~ /^\s*close/ ||
- $pragma =~ /close/ ) {
- $self->{ _status } = "closed";
- $self->_set_status_info("closed");
- Log("ticket is closed");
- }
- else {
- Log("ticket status not changed");
- }
-}
-
-
-sub update_db
-{
- my ($self, $curproc, $args) = @_;
- my $config = $curproc->{ config };
- my $ml_name = $config->{ ml_name };
-
- return if $self->{ _pragma } eq 'ignore';
-
- $self->open_db($curproc, $args);
-
- # save $ticke_id et.al. in db_dir/$ml_name
- $self->_update_db($curproc, $args);
-
- # save cross reference pointers among $ml_name
- $self->_update_index_db($curproc, $args);
-
- $self->close_db($curproc, $args);
-}
-
-
-sub _prepare_subject_ticket_id
-{
- my ($self, $header, $config, $id) = @_;
- my $ml_name = $config->{ ml_name };
-
- # ticket_id in subject
- my $tag = $config->{ ticket_subject_tag };
- my $ticket_id = sprintf($tag, $id);
- $self->{ _ticket_subject_tag } = $ticket_id;
-
- $tag = $config->{ ticket_id_syntax };
- $ticket_id = sprintf($tag, $id);
- $self->{ _ticket_id } = $ticket_id;
-
- return $ticket_id;
-}
-
-
-sub _extract_ticket_id
-{
- my ($self, $header, $config) = @_;
- my $tag = $config->{ ticket_subject_tag };
- my $subject = $header->get('subject');
-
- use FML::Header::Subject;
- my $obj = new FML::Header::Subject;
- my $regexp = $obj->regexp_compile($tag);
-
- # Subject: ... [ticket_id]
- if (($config->{ ticket_subject_tag_location } eq 'appended') &&
- ($subject =~ /($regexp)\s*$/)) {
- my $id = $1;
- $id =~ s/^(\[|\(|\{)//;
- $id =~ s/(\]|\)|\})$//;
- return $id;
- }
- # XXX incomplete, we check subject after cutting off "Re:" et. al.
- # Subject: [ticket_id] ...
- # Subject: Re: [ticket_id] ...
- elsif (($config->{ ticket_subject_tag_location } eq 'appended') &&
- ($subject =~ /^\s*($regexp)/)) {
- my $id = $1;
- $id =~ s/^(\[|\(|\{)//;
- $id =~ s/(\]|\)|\})$//;
- return $id;
- }
- else {
- Log("no ticket id /$regexp/ in subject");
- return 0;
- }
-}
-
-
-sub _rewrite_header
-{
- my ($self, $header, $config, $id) = @_;
-
- # create ticket syntax in the subject
- $self->_prepare_subject_ticket_id($header, $config, $id);
-
- # append the ticket tag to the subject
- my $subject = $header->get('subject') || '';
- $header->replace('Subject',
- $subject." " . $self->{ _ticket_subject_tag });
-}
-
-
-sub _update_db
-{
- my ($self, $curproc, $args) = @_;
- my $config = $curproc->{ config };
- my $pcb = $curproc->{ pcb };
- my $article_id = $pcb->get('article', 'id');
- my $ticket_id = $self->{ _ticket_id };
-
- # 0. logging
- Log("article_id=$article_id ticket_id=$ticket_id");
-
- # prepare hash table tied to db_dir/*db's
- my $rh = $self->{ _hash_table };
-
- # 1.
- $rh->{ _ticket_id }->{ $article_id } = $ticket_id;
- $rh->{ _date }->{ $article_id } = time;
- $rh->{ _articles }->{ $ticket_id } .= $article_id . " ";
-
- # 2. record the sender information
- my $header = $curproc->{ incoming_message }->{ header };
- $rh->{ _sender }->{ $article_id } = $header->get('from');
-
- # 3. update status information
- if (defined $self->{ _status }) {
- $self->_set_status($ticket_id, $self->{ _status });
- }
- else {
- # set the default status value for the first time.
- unless (defined $rh->{ _status }->{ $ticket_id }) {
- $self->_set_status($ticket_id, 'open');
- }
- }
-
- # 4. save optional/additional information
- # message_id hash is { message_id => ticket_id };
- my $mid = $header->get('message-id');
- $mid = $header->address_clean_up($mid);
- $rh->{ _message_id }->{ $mid } = $ticket_id;
-
- # 5. history
- my $buf = '';
- my (@aid) = split(/\s+/, $rh->{ _articles }->{ $ticket_id });
- my $sender = $rh->{ _sender }->{ $aid[0] };
- my $when = $rh->{ _date }->{ $aid[0] };
-
- # clean up
- $sender =~ s/[\s\n]*$//;
- $when =~ s/[\s\n]*$//;
-
- use FML::Date;
- $when = FML::Date->new($when)->mail_header_style();
-
- $buf .= "\t\n";
- $buf .= "\tthis ticket/thread is opended at article $aid[0]\n";
- $buf .= "\tby $sender\n";
- $buf .= "\ton $when\n";
- $buf .= "\tarticle references: @aid\n";
- $self->{ _status_history } = $buf;
-}
-
-
-
-# register myself to index_db for further reference among mailing lists
-sub _update_index_db
-{
- my ($self, $curproc, $args) = @_;
- my $config = $curproc->{ config };
- my $ticket_id = $self->{ _ticket_id };
- my $rh = $self->{ _hash_table };
- my $ml_name = $config->{ ml_name };
-
- my $ref = $rh->{ _index }->{ $ticket_id } || '';
- if ($ref !~ /^$ml_name|\s$ml_name\s|$ml_name$/) {
- $rh->{ _index }->{ $ticket_id } .= $ml_name." ";
- }
-}
-
-
-=head2 C<open_db($curproc, $args)>
-
-open DB.
-It uses tie() to bind a hash to a DB file.
-Our minimal_states uses several DB files for
-C<%ticket_id>,
-C<%date>,
-C<%status>,
-C<%sender>,
-C<%articles>,
-C<%message_id>
-and
-C<%index>.
-
-=head2 C<close_db($curproc, $args)>
-
-untie() corresponding hashes opened by C<open_db()>.
-
-=cut
-
-
-sub open_db
-{
- my ($self, $curproc, $args) = @_;
- my $config = $curproc->{ config };
- my $pcb = $curproc->{ pcb };
- my $db_type = $curproc->{ ticket_db_type } || 'AnyDBM_File';
- my $db_dir = $self->{ _db_dir };
-
- my (%ticket_id, %date, %status, %articles, %sender, %message_id, %index);
- my $ticket_id_file = $db_dir.'/ticket_id';
- my $date_file = $db_dir.'/date';
- my $status_file = $db_dir.'/status';
- my $sender_file = $db_dir.'/sender';
- my $articles_file = $db_dir.'/articles';
- my $message_id_file = $db_dir.'/message_id';
- my $index_file = $self->{ _index_db };
-
- eval qq{ use $db_type;};
- unless ($@) {
- eval q{
- use Fcntl;
- tie %ticket_id, $db_type, $ticket_id_file, O_RDWR|O_CREAT, 0644;
- tie %date, $db_type, $date_file, O_RDWR|O_CREAT, 0644;
- tie %status, $db_type, $status_file, O_RDWR|O_CREAT, 0644;
- tie %sender, $db_type, $sender_file, O_RDWR|O_CREAT, 0644;
- tie %articles, $db_type, $articles_file, O_RDWR|O_CREAT, 0644;
- tie %index, $db_type, $index_file, O_RDWR|O_CREAT, 0644;
- tie %message_id, $db_type, $message_id_file, O_RDWR|O_CREAT, 0644;
- };
- unless ($@) {
- $self->{ _hash_table }->{ _ticket_id } = \%ticket_id;
- $self->{ _hash_table }->{ _date } = \%date;
- $self->{ _hash_table }->{ _status } = \%status;
- $self->{ _hash_table }->{ _sender } = \%sender;
- $self->{ _hash_table }->{ _articles } = \%articles;
- $self->{ _hash_table }->{ _message_id } = \%message_id;
- $self->{ _hash_table }->{ _index } = \%index;
- }
- else {
- Log("Error: tail to tie() under $db_type");
- return undef;
- }
- }
- else {
- Log("Error: fail to use $db_type");
- return undef;
- }
-
- 1;
-}
-
-
-sub close_db
-{
- my ($self, $curproc, $args) = @_;
-
- my $ticket_id = $self->{ _hash_table }->{ _ticket_id };
- my $date = $self->{ _hash_table }->{ _date };
- my $status = $self->{ _hash_table }->{ _status };
- my $sender = $self->{ _hash_table }->{ _sender };
- my $articles = $self->{ _hash_table }->{ _articles };
- my $index = $self->{ _hash_table }->{ _index };
- my $message_id = $self->{ _hash_table }->{ _message_id };
-
- untie %$message_id;
- untie %$ticket_id;
- untie %$date;
- untie %$status;
- untie %$sender;
- untie %$articles;
- untie %$index;
-}
-
-
-=head2 C<set_status($args)>
-
-set $status for $ticket_id. It rewrites DB (file).
-C<$args>, HASH reference, must have two keys.
-
- $args = {
- ticket_id => $ticket_id,
- status => $status,
- }
-
-C<set_status()> calls open_db() an close_db() automatically within it.
-
-=cut
-
-
-# Descriptions:
-# Arguments: $self $curproc $args
-# Side Effects:
-# Return Value: none
-sub set_status
-{
- my ($self, $curproc, $args) = @_;
- my $ticket_id = $args->{ ticket_id };
- my $status = $args->{ status };
-
- Log("ticket.set_status($ticket_id, $status)");
-
- $self->open_db($curproc, $args);
- $self->_set_status($ticket_id, $status);
- $self->close_db($curproc, $args);
-}
-
-
-sub _set_status
-{
- my ($self, $ticket_id, $value) = @_;
- $self->{ _hash_table }->{ _status }->{ $ticket_id } = $value;
-}
-
-
-
-=head1 OUTPUT ROUTINES
-
-=head2 C<show_summary($curproc, $args>)
-
-show the ticket summary.
-See L<_simple_print()> for more detail.
-Internally either of
-C<_simple_print()>
-or
-C<_summary_print()>
-is used for purposes.
-
-Each row that C<show_summary()> returns has a set of
-C<date>, C<age>, C<status>, C<ticket-id> and
-C<articles>, which is a list of articles with the ticket-id.
-
-=head2 C<_simple_print()>
-
-show entries by the ticket_id order. For example,
-
- date age status ticket id articles
- ------------------------------------------------------------
- 2001/02/07 3.6 going elena_#00000450 807 808 809
- 2001/02/07 3.1 open elena_#00000451 810
- 2001/02/07 3.0 open elena_#00000452 812
- 2001/02/07 3.0 open elena_#00000453 813
- 2001/02/07 3.0 going elena_#00000454 814 815
- 2001/02/10 0.1 open elena_#00000456 821
-
-=head2 C<_summary_print()>
-
-show entries in the C<cost> larger order.
-The cost is evaluated by $status and $age.
-The cost is larger as $age is larger.
-It is also larger if $status is C<open>.
-
-=cut
-
-
-sub show_summary
-{
- my ($self, $curproc, $args, $optargs) = @_;
- my $mode = $self->{ _mode } || 'text';
- my $ml_name = $curproc->{ config }->{ ml_name };
-
- print "<TABLE BORDER=4>\n" if $mode eq 'html';
- $self->_show_summary($curproc, $args, $optargs);
- print "</TABLE>\n" if $mode eq 'html';
-}
-
-
-sub _show_summary
-{
- my ($self, $curproc, $args) = @_;
- my ($tid, $status, $ticket_id);
- my $mode = $self->{ _mode } || 'text';
-
- # rh: ticket id list, which is ARRAY REFERENCE tied to db_dir/*db's
- $ticket_id = $self->get_id_list($curproc, $args);
-
- # self->{ _hash_table } is tied to DB's.
- $self->open_db($curproc, $args);
-
- if (@$ticket_id) {
- # sort the ticket output order it out by cost
- # and print the ticket summary in that order.
- $self->sort($curproc, $args, $ticket_id);
-
- if ($mode eq 'html') {
- $self->_print_ticket_summary($curproc, $args, $ticket_id);
- }
- else {
- $self->_print_ticket_summary($curproc, $args, $ticket_id);
-
- # show short summary for each article
- $self->_print_article_summary($curproc, $args, $ticket_id);
- }
- }
-
- # self->{ _hash_table } is untied from DB's.
- $self->close_db($curproc, $args);
-}
-
-
-# return @ticket_id ARRAY
-sub get_id_list
-{
- my ($self, $curproc, $args) = @_;
- my ($tid, $status, @ticket_id);
-
- # self->{ _hash_table } is tied to DB's.
- $self->open_db($curproc, $args);
-
- my $rh_status = $self->{ _hash_table }->{ _status };
- my $mode = $args->{ mode } || 'default';
-
- TICEKT_LIST:
- while (($tid, $status) = each %$rh_status) {
- if ($mode eq 'default') {
- next TICEKT_LIST if $status =~ /close/o;
- }
-
- push(@ticket_id, $tid);
- }
-
- $self->close_db($curproc, $args);
-
- \@ticket_id;
-}
-
-
-sub sort
-{
- my ($self, $curproc, $args, $ticket_id) = @_;
-
- # get age HASH TABLE
- my ($age, $cost) = $self->_calculate_age($curproc, $args, $ticket_id);
- $self->{ _age } = $age;
- $self->{ _cost } = $cost;
-
- $self->_sort_ticket_id($curproc, $args, $ticket_id, $cost);
-}
-
-
-sub _sort_ticket_id
-{
- my ($self, $curproc, $args, $ticket_id, $cost) = @_;
-
- @$ticket_id = sort {
- $cost->{$b} cmp $cost->{$a};
- } @$ticket_id;
-}
-
-
-sub _calculate_age
-{
- my ($self, $curproc, $args, $ticket_id) = @_;
- my (%age, %cost) = ();
- my $now = time; # save the current UTC for convenience
- my $rh = $self->{ _hash_table } || {};
- my $day = 24*3600;
-
- # $age hash referehence = { $ticket_id => $age };
- my (@aid, $last, $age, $date, $status, $tid) = ();
- for $tid (sort @$ticket_id) {
- # $last: get the latest one of article_id's
- (@aid) = split(/\s+/, $rh->{ _articles }->{ $tid });
- $last = $aid[ $#aid ] || 0;
-
- # how long this ticket is not concerned ?
- $age = sprintf("%2.1f%s", ($now - $rh->{ _date }->{ $last })/$day);
- $age{ $tid } = $age;
-
- # evaluate cost hash table which is { $ticket_id => $cost }
- $cost{ $tid } = $rh->{ _status }->{ $tid }.'-'. $age;
- }
-
- return (\%age, \%cost);
-}
-
-
-sub _print_ticket_summary
-{
- my ($self, $curproc, $args, $ticket_id) = @_;
- my $mode = $self->{ _mode } || 'text';
- my $rh_age = $self->{ _age } || {};
- my $fd = $self->{ _fd } || \*STDOUT;
- my $rh = $self->{ _hash_table };
- my $format = "%10s %5s %6s %-20s %s\n";
-
- if ($mode eq 'text') {
- printf($fd $format, 'date', 'age', 'status', 'ticket id', 'articles');
- print $fd "-" x60;
- print $fd "\n";
- }
- else {
- print "<TD>action\n";
- print "<TD>date\n"."<TD>age\n"."<TD>status\n"."<TD>ticket id\n";
- print "<TD>article summary\n";
- }
-
- my ($tid, @article_id, $article_id, $date, $age, $status) = ();
- my $dh = new FML::Date;
- for $tid (@$ticket_id) {
- # get the first $article_id from the article_id list
- (@article_id) = split(/\s+/, $rh->{ _articles }->{ $tid });
- $article_id = $article_id[0];
-
- # determine $date for the $article_id
- # $age and $status for $ticket_id
- $date = $dh->YYYYxMMxDD( $rh->{ _date }->{ $article_id } , '/');
- $age = $rh_age->{ $tid };
- $status = $rh->{ _status }->{ $tid };
-
- if ($mode eq 'html') {
- $self->_show_ticket_by_html_table($curproc, $args, {
- format => $format,
- date => $date,
- age => $age,
- status => $status,
- tid => $tid,
- articles => $rh->{ _articles }->{ $tid },
- });
- }
- else {
- printf($fd $format,
- $date, $age, $status, $tid, $rh->{ _articles }->{ $tid });
- }
- }
-}
-
-
-sub _cost_to_indicator
-{
- my ($cost) = @_;
- my $how_bad = 0;
-
- if ($cost =~ /(\w+)\-(\d+)/) {
- $how_bad += $2;
- $how_bad += 2 if $1 =~ /open/;
- $how_bad = "!" x ($how_bad > 6 ? 6 : $how_bad);
- }
-}
-
-
-sub _print_article_summary
-{
- my ($self, $curproc, $args, $ticket_id) = @_;
- my $age = $self->{ _age } || {};
- my $cost = $self->{ _cost } || {};
- my $fd = $self->{ _fd } || \*STDOUT;
- my $rh = $self->{ _hash_table };
-
- print $fd "\n\"!\" mark: stalled? please check and reply it.\n";
-
- my ($aid, @aid);
- my $spool_dir = $curproc->{ config }->{ spool_dir };
- for my $tid (@$ticket_id) {
- my $how_bad = _cost_to_indicator( $cost->{ $tid } );
- printf $fd "\n%6s %-10s %s\n", $how_bad, $tid;
-
- (@aid) = split(/\s+/, $rh->{ _articles }->{ $tid });
- $aid = $aid[0];
- print $fd $self->_article_summary(File::Spec->catfile($spool_dir,
- $aid));
- }
-}
-
-
-sub _article_summary
-{
- my ($self, $file) = @_;
- my (@header) = ();
- my $buf = '';
- my $line = 5;
- my $mode = $self->{ _mode } || 'text';
- my $padding = $mode eq 'text' ? ' # ' : '';
-
- use FileHandle;
- my $fh = new FileHandle $file;
-
- if (defined $fh) {
- LINE:
- while (<$fh>) {
- # nuke useless lines
- next LINE if /^\>/;
- next LINE if /^\-/;
-
- if (1 ../^$/) {
- push(@header, $_);
- }
- else {
- next LINE if /^\s*$/;
-
- # ignore mail header like patterns
- next LINE if /^X-[-A-Za-z0-9]+:/i;
- next LINE if /^Return-[-A-Za-z0-9]+:/i;
- next LINE if /^Mime-[-A-Za-z0-9]+:/i;
- next LINE if /^Content-[-A-Za-z0-9]+:/i;
- next LINE if /^(To|From|Subject|Reply-To|Received):/i;
- next LINE if /^(Message-ID|Date):/i;
-
- # pick up effetive the first $line lines
- if ($line-- > 0) {
- $buf .= $padding. $_;
- }
- else {
- last LINE;
- }
- }
- }
- close($fh);
- }
-
- use FML::Header;
- my $h = new FML::Header \@header;
- my $header_info = $self->_header_summary({
- header => $h,
- padding => $padding,
- });
-
- return $header_info . $buf;
-}
-
-
-sub _header_summary
-{
- my ($self, $args) = @_;
- my $from = $args->{ header }->get('from');
- my $subject = $args->{ header }->get('subject');
- my $padding = $args->{ padding };
-
- use FML::MIME qw(decode_mime_string);
- $subject = decode_mime_string($subject, { charset => 'euc-japan' });
- $subject =~ s/\n/ /g;
- $subject = FML::Header->delete_subject_tag_like_string($subject);
-
- $from = decode_mime_string($from, { charset => 'euc-japan' });
- $from =~ s/\n/ /g;
-
- my $br = $self->{ _mode } eq 'html' ? '<BR>' : '';
- return
- $padding. " From: ". $from ."$br\n".
- $padding. "Subject: ". $subject ."$br\n";
-}
-
-
-sub show_articles_for_ticket
-{
- my ($self, $curproc, $args, $ticket_id) = @_;
- my $mode = $self->{ _mode } || 'text';
- my $config = $curproc->{ config };
- my $spool_dir = $config->{ spool_dir };
-
- $self->open_db($curproc, $args);
-
- my $articles = $self->{ _hash_table }->{ _articles }->{ $ticket_id };
-
- print "<B>" if $mode eq 'html';
- print "show contents related with ticket_id=$ticket_id\n";
- print "</B><HR><PRE>\n" if $mode eq 'html';
-
- use FileHandle;
- use FML::Language::ISO2022JP qw(STR2EUC);
-
- my $s = '';
- for (split(/\s+/, $articles)) {
- my $file = File::Spec->catfile($spool_dir, $_);
-
- my $fh = new FileHandle $file;
- while (defined($_ = $fh->getline())) {
- next if 1 .. /^$/;
-
- $s = STR2EUC($_);
- $s =~ s/&/&amp;/g;
- $s =~ s/</&lt;/g;
- $s =~ s/>/&gt;/g;
- $s =~ s/\"/&quot;/g;
- print $s;
- }
- $fh->close;
- }
-
- print "</PRE>" if $mode eq 'html';
-
- $self->close_db($curproc, $args);
-}
-
-
-sub cgi_top_menu
-{
- my ($self, $curproc, $args) = @_;
- my $config = $curproc->{ config };
- my $action = 'fmlticket.cgi';
- my $target = $config->{ ticket_cgi_target_window } || 'TicketCGIWindow';
-
- use DirHandle;
- my $dh = new DirHandle $config->{ ml_home_prefix };
- my @dirlist;
- my $prefix = $config->{ ml_home_prefix };
- while ($_ = $dh->read()) {
- next if /^\./;
- next if /^\@/;
- push(@dirlist, $_) if -f "$prefix/$_/config.cf";
- }
- $dh->close;
-
- if ($self->{ _mode } eq 'html') {
- require 'ctime.pl';
- my $time = ctime(time);
- my $ml_name = $config->{ ml_name };
- print "[$time] the brief summary for \"$ml_name\" ML<BR>";
- }
-
- use CGI qw/:standard/;
- print start_form(-action=>$action, -target=>$target);
- print "mailing list: ",
- popup_menu(-name => 'ml_name', -values => \@dirlist),
- submit(-name => 'go'),
- end_form;
-}
-
-
-
-# This shows summary on C<$ticket_id> in HTML language.
-# It is used in C<FML::CGI::TicketSystem>.
-sub _show_ticket_by_html_table
-{
- my ($self, $curproc, $args, $optargs) = @_;
- my $config = $curproc->{ config };
- my $ml_name = $config->{ ml_name };
- my $spool_dir = $config->{ spool_dir };
- my $action = 'fmlticket.cgi';
- my $target = $config->{ ticket_cgi_target_window } || 'TicketCGIWindow';
-
- # printf($fd $format,
- # $date, $age, $status, $tid, $rh->{ _articles }->{ $tid });
- my $format = $optargs->{ format };
- my $date = $optargs->{ date };
- my $age = $optargs->{ age };
- my $status = $optargs->{ status };
- my $tid = $optargs->{ tid };
- my $articles = $optargs->{ articles };
- my $aid = (split(/\s+/, $articles))[0];
-
- # do nothing if the $ticket_id is unknown.
- return unless $tid;
-
- # <FORM ACTION=> ..>
- my $xtid = CGI::escape($tid);
- $action = "${action}?ml_name=${ml_name}";
- $action .= "&ticket_id=$xtid&article_id=$aid";
-
- print "<TR>\n";
- print "<TD>";
- print "<A HREF=\"$action&action=close\" TARGET=\"$target.close\">";
- print "[close]</A>\n";
- print "<BR>\n";
- print "<A HREF=\"$action&action=show\" TARGET=\"$target.show\">";
- print "[see articles]</A>\n";
- print "<TD>$date\n";
- print "<TD>$age\n";
- print "<TD>$status\n";
- print "<TD>$tid\n";
- print "<TD>";
-
- $aid = (split(/\s+/, $articles))[0];
- my $buf = $self->_article_summary(File::Spec->catfile($spool_dir, $aid));
- use FML::Language::ISO2022JP qw(STR2EUC);
- print STR2EUC($buf);
-}
-
-
-=head2 C<info($curproc, $args)>
-
-=cut
-
-sub add_info
-{
- my ($self, $curproc, $args) = @_;
- my $config = $curproc->{ config };
- my $header = $curproc->{ article }->{ header };
-
- if (defined $self->{ _status_info }) {
- $header->add('X-Ticket-Status', $self->{ _status_info });
- }
-
- if (defined $self->{ _ticket_id }) {
- $header->add('X-Ticket-ID', $self->{ _ticket_id });
- }
-
- if (defined $self->{ _status_history }) {
- $header->add('X-Ticket-History', $self->{ _status_history });
- }
-}
-
-
-=head2 C<run_cgi($curproc, $args)>
-
-execute CGI.
-
-=cut
-
-sub run_cgi
-{
- my ($self, $curproc, $args) = @_;
- my $config = $curproc->{ config };
- my $title = $config->{ ticket_cgi_title } || 'ticket system interface';
- my $color = $config->{ ticket_cgi_bgcolor } || '#E6E6FA';
-
- # XXX $ml_name may change by HTTP request
- $config->{ ml_name } = param('ml_name') if param('ml_name');
-
- # ensure the current mode
- $self->mode({ mode => 'html' });
-
- # load standard CGI routines
- use CGI qw/:standard/;
-
- # get action parameter via HTTP
- my $action = param('action') || 'list';
- my $ticket_id = param('ticket_id');
-
- # o.k start html
- print start_html(-title=>$title,-BGCOLOR=>$color), "\n";
-
- if ($action eq 'close') {
- $self->set_status($curproc, {
- ticket_id => $ticket_id,
- status => 'closed',
- });
- }
-
- if ($action eq 'show') {
- Log("run.cgi.show_articles for $ticket_id");
- $self->show_articles_for_ticket($curproc, $args, $ticket_id);
- }
- else {
- # menu at the top of scrren
- $self->cgi_top_menu($curproc, $args);
-
- # show summary
- $self->show_summary($curproc, $args);
- }
-
- # o.k. end of html
- print end_html;
- print "\n";
-}
-
-
-=head1 AUTHOR
-
-Ken'ichi Fukamachi
-
-=head1 COPYRIGHT
-
-Copyright (C) 2001 Ken'ichi Fukamachi
-
-All rights reserved. This program is free software; you can
-redistribute it and/or modify it under the same terms as Perl itself.
-
-=head1 HISTORY
-
-FML::Ticket::Model::minimal_states appeared in fml5 mailing list driver package.
-See C<http://www.fml.org/> for more details.
-
-=cut
-
-1;
diff --git a/fml/lib/FML/Ticket/System.pm b/fml/lib/FML/Ticket/System.pm
deleted file mode 100644
index 442e79db..00000000
--- a/fml/lib/FML/Ticket/System.pm
+++ /dev/null
@@ -1,164 +0,0 @@
-#-*- perl -*-
-#
-# Copyright (C) 2001 Ken'ichi Fukamachi
-# All rights reserved. This program is free software; you can
-# redistribute it and/or modify it under the same terms as Perl itself.
-#
-# $FML: System.pm,v 1.26 2001/10/22 14:10:07 fukachan Exp $
-#
-
-package FML::Ticket::System;
-
-use strict;
-use vars qw(@ISA @EXPORT @EXPORT_OK $AUTOLOAD);
-use Carp;
-use ErrorStatus qw(error_set error error_clear);
-use FML::Log qw(Log LogWarn LogError);
-
-
-=head1 NAME
-
-FML::Ticket::System - ticket system core engine
-
-=head1 SYNOPSIS
-
- use FML::Ticket::Model::minimal_states;
- $ticket = new FML::Ticket::Model::minimal_states;
- $ticket->assign($curproc, $args);
- $ticket->update_cache($curproc, $args);
-
-=head1 DESCRIPTION
-
-the base class of ticket systems.
-This module provides basic functions to help sub classes.
-
-=head2 CLASS HIERARCHY
-
- FML::Ticket::System
- |
- A
- -------------------
- | |
- A A
- minimal_states
-
-=head1 METHODS
-
-=head2 C<new()>
-
-constructor.
-
-=cut
-
-
-# Descriptions: constructor
-# Arguments: $self
-# Side Effects: none
-# Return Value: object
-sub new
-{
- my ($self) = @_;
- my ($type) = ref($self) || $self;
- my $me = {};
- return bless $me, $type;
-}
-
-
-# Descriptions: set up directory which is taken from
-# $self->{ _db_dir }
-# Arguments: $self $curproc $args
-# Side Effects: create a "_db_dir" directory if needed
-# Return Value: 1 (success) or undef (fail)
-sub _init_ticket_db_dir
-{
- my ($self, $curproc, $args) = @_;
- my $config = $curproc->{ config };
-
- if (defined $self->{ _db_dir }) {
- my $db_dir = $self->{ _db_dir };
- unless (-d $db_dir) {
- use File::Utils qw(mkdirhier);
- mkdirhier($db_dir, $config->{ default_directory_mode }) || do {
- $self->error_set( File::Utils->error() );
- return undef;
- };
- }
- }
-
- return 1;
-}
-
-
-=head2 C<increment_id(file)>
-
-increment ticket number which is taken up from C<file>
-and save its new number to C<file>.
-
-=cut
-
-
-# Descriptions: increment ticket number $id holded in $seq_file
-# Arguments: $self $seq_file
-# Side Effects: increment id holded in $seq_file
-# Return Value: number
-sub increment_id
-{
- my ($self, $seq_file) = @_;
-
- use File::Sequence;
- my $sfh = new File::Sequence { sequence_file => $seq_file };
- my $id = $sfh->increment_id;
- $self->error_set( $sfh->error );
-
- $id;
-}
-
-
-=head1 REFERENCES
-
-=head2 ticket status ("RT" case)
-
-A Request will always be in one of the following four states:
-
- Open -- the Request is expecting imminent action a/o updates
- Stalled -- the Request needs a specific action or piece of
- information before it can proceed
- Resolved -- the Request has either been answered or successfully
- taken care of, and no longer needs action
- Dead -- the request should not have been in the ticketing system to begin
- with and has been completely purged.
-
-=head2 ticket status ("REQ" case)
-
- Another somewhat hardcoded features is the "status" field.
- We're not exactly sure how to use this yet, but normally
- use "stalled" to indicate that this request isn't one can
- make any progress at the moment, thus isn't worth picking
- from the queue to work on.
-
-
-=head1 SEE ALSO
-
-L<File::Utils>,
-L<File::Sequence>.
-
-=head1 AUTHOR
-
-Ken'ichi Fukamachi
-
-=head1 COPYRIGHT
-
-Copyright (C) 2001 Ken'ichi Fukamachi
-
-All rights reserved. This program is free software; you can
-redistribute it and/or modify it under the same terms as Perl itself.
-
-=head1 HISTORY
-
-FML::Ticket::System appeared in fml5 mailing list driver package.
-See C<http://www.fml.org/> for more details.
-
-=cut
-
-
-1;