File Coverage

blib/lib/CGI/Listman.pm
Criterion Covered Total %
statement 24 415 5.7
branch 0 168 0.0
condition 0 63 0.0
subroutine 8 64 12.5
pod 11 13 84.6
total 43 723 5.9


line stmt bran cond sub pod time code
1             # Listman.pm - this file is part of the CGI::Listman distribution
2             #
3             # CGI::Listman is Copyright (C) 2002 iScream multimédia
4             #
5             # This package is free software; you can redistribute it and/or
6             # modify it under the same terms as Perl itself.
7             #
8             # Author: Wolfgang Sourdeau
9              
10             # For a schematic description of the classes implemented in this file,
11             # have a look at the file "schema.txt".
12              
13             package CGI::Listman;
14              
15 1     1   4458 use strict;
  1         2  
  1         25  
16              
17 1     1   4 use Carp;
  1         2  
  1         40  
18 1     1   1282 use DBI;
  1         16696  
  1         62  
19              
20 1     1   8 use vars qw($VERSION);
  1         1  
  1         2092  
21              
22             $VERSION = '0.02';
23              
24             sub new {
25 0     0 1   my $class = shift;
26              
27 0           my $self = {};
28 0           $self->{'dbi_backend'} = shift;
29 0           $self->{'list_name'} = shift;
30 0           $self->{'list_dir'} = shift;
31 0           $self->{'table_name'} = $self->{'list_name'};
32 0           $self->{'db_name'} = undef;
33 0           $self->{'db_uname'} = undef;
34 0           $self->{'db_passwd'} = undef;
35 0           $self->{'db_host'} = undef;
36 0           $self->{'db_port'} = undef;
37              
38 0           $self->{'list'} = undef;
39 0           $self->{'_dbi_params'} = undef;
40 0           $self->{'_dictionary'} = undef;
41 0           $self->{'_last_line_number'} = 0;
42 0           $self->{'_loading_list'} = undef;
43              
44 0           bless $self, $class;
45             }
46              
47             sub set_backend {
48 0     0 1   my ($self, $backend) = @_;
49              
50 0 0         if (defined $self->{'dbi_backend'}) {
51             print STDERR "A backend is already defined ("
52 0           .$self->{'dbi_backend'}.") for this CGI::Listman instance.\n"
53             } else {
54 0           eval "use DBD::".$backend.";";
55 0 0         die "This backend is not available:\n".$@ if ($@);
56 0           $self->{'dbi_backend'} = $backend;
57             }
58             }
59              
60             sub set_list_name {
61 0     0 1   my ($self, $list_name) = @_;
62              
63 0 0         if (defined $self->{'list_name'}) {
64             print STDERR "A list name is already defined ("
65 0           .$self->{'list_name'}.") for this instance of CGI::Listman.\n";
66             } else {
67 0           $self->{'list_name'} = $list_name;
68             $self->{'table_name'} = $list_name
69 0 0         unless (defined $self->{'table_name'});
70             }
71             }
72              
73             sub set_table_name {
74 0     0 1   my ($self, $table_name) = @_;
75              
76 0 0         if (defined $self->{'table_name'}) {
77 0           $self->{'table_name'} = $table_name;
78             }
79             }
80              
81             sub dictionary {
82 0     0 1   my $self = shift;
83              
84 0 0         unless (defined $self->{'_dictionary'}) {
85             die "List directory not defined for this instance of CGI::Listman.\n"
86 0 0         unless (defined $self->{'list_dir'});
87             die "List filename not defined for this instance of CGI::Listman.\n"
88 0 0         unless (defined $self->{'list_name'});
89              
90 0           my $path = $self->{'list_dir'}.'/'.$self->{'list_name'}.'.dict';
91 0 0         die "No dictionary ('".$self->{'list_name'}.".dict')\n"
92             unless (-f $path);
93              
94 0           my $dictionary = CGI::Listman::dictionary->new ($path);
95              
96 0           $self->{'_dictionary'} = $dictionary;
97             }
98              
99 0           return $self->{'_dictionary'};
100             }
101              
102             sub seek_line_by_num {
103 0     0 1   my ($self, $number) = @_;
104              
105 0 0         $self->load_lines () unless (defined $self->{'list'});
106              
107 0           my $ret_line = undef;
108 0           my $list_ref = $self->{'list'};
109              
110 0           foreach my $line (@$list_ref) {
111 0 0         if ($line->number () == $number) {
112 0           $ret_line = $line;
113 0           last;
114             }
115             }
116              
117 0           return $ret_line;
118             }
119              
120             sub _dbi_setup {
121 0     0     my $self = shift;
122              
123 0 0         unless (defined $self->{'_dbi_params'}) {
124             die "No backend specified for this instance of CGI::Listman.\n"
125 0 0         unless (defined $self->{'dbi_backend'});
126 0 0         if ($self->{'dbi_backend'} eq 'CSV') {
127 0           $self->{'_dbi_params'} = ":f_dir=".$self->{'list_dir'};
128 0 0         unless (-f $self->{'list_dir'}.'/'.$self->{'table_name'}.'.csv') {
129             open my $list_file, '>'
130 0           .$self->{'list_dir'}.'/'.$self->{'table_name'}.'.csv';
131 0           close $list_file;
132             }
133             } else {
134             die "Sorry, this DBI backend \"".$self->{'dbi_backend'}
135             ."\" is not handled at this time.\n"
136 0 0         unless ($self->{'dbi_backend'} eq 'mysql');
137 0           my $dbi_params = ":database=".$self->{'db_name'};
138             $dbi_params .= ":host=".$self->{'db_host'}
139 0 0 0       if (defined $self->{'db_host'} && $self->{'db_host'} ne '');
140             $dbi_params .= ":port=".$self->{'db_port'}
141 0 0 0       if (defined $self->{'db_port'} && $self->{'db_port'} ne '');
142 0           $self->{'_dbi_params'} = $dbi_params;
143             }
144             }
145             }
146              
147             sub _db_fields_setup {
148 0     0     my $self = shift;
149              
150 0 0         unless (defined $self->{'_db_fields'}) {
151 0           my @fields = ('number', 'timestamp', 'seen', 'exported');
152 0           my $dictionary = $self->dictionary ();
153 0           my $dict_terms = $dictionary->terms ();
154              
155 0           foreach my $term (@$dict_terms) {
156 0           push @fields, $term->{'key'};
157             }
158 0           $self->{'_db_fields'} = \@fields;
159             }
160             }
161              
162             sub _db_connect {
163 0     0     my $self = shift;
164              
165 0 0         unless (defined $self->{'_db_connection'}) {
166 0           $self->_dbi_setup ();
167 0           $self->_db_fields_setup ();
168             my $dbh = DBI->connect ("DBI:"
169             .$self->{'dbi_backend'}
170             .$self->{'_dbi_params'},
171             $self->{'db_uname'},
172 0 0         $self->{'db_passwd'})
173             or die DBI->errstr;
174 0 0         if ($self->{'dbi_backend'} eq 'CSV') {
175             $dbh->{'csv_tables'}->{$self->{'table_name'}} =
176             {'col_names' => $self->{'_db_fields'},
177 0           'file' => $self->{'table_name'}.".csv"};
178             }
179 0           $self->{'_db_connection'} = $dbh;
180             }
181             }
182              
183             sub _get_line_numbers {
184 0     0     my $self = shift;
185              
186 0           my @numbers;
187              
188 0 0         if (defined $self->{'list'}) {
189 0           my $list_ref = $self->{'list'};
190              
191 0           foreach my $line (@$list_ref) {
192 0           push @numbers, $line->number ();
193             }
194             }
195              
196 0           return @numbers;
197             }
198              
199             sub add_line {
200 0     0 0   my ($self, $line) = @_;
201              
202             $self->load_lines ()
203             unless (defined $self->{'list'}
204 0 0 0       || defined $self->{'_loading_list'});
205              
206             $line->{'number'} = $self->{'_last_line_number'} + 1
207 0 0         unless ($line->{'number'});
208              
209 0           my @numbers = $self->_get_line_numbers ();
210             croak "This instance's list of lines already contains a line with"
211 0 0         ." this number (".$line->{'number'}.").\n"
212             if (grep (m/$line->{'number'}/, @numbers));
213              
214 0           $self->{'_last_line_number'} = $line->{'number'};
215              
216 0 0         unless (defined $self->{'list'}) {
217 0           my @new_list;
218 0           $self->{'list'} = \@new_list;
219             }
220              
221 0           my $list_ref = $self->{'list'};
222 0           push @$list_ref, $line;
223             }
224              
225             sub load_lines {
226 0     0 1   my $self = shift;
227              
228 0           $self->{'_loading_list'} = 1;
229 0           $self->_db_connect ();
230              
231 0           my $dbh = $self->{'_db_connection'};
232              
233             my $row_list =
234 0 0         $dbh->selectall_arrayref ("SELECT * FROM ".$self->{'table_name'})
235             or die $dbh->errstr;
236              
237             # die $row_list->[0];
238 0 0         delete $self->{'list'} if (defined $self->{'list'});
239              
240 0 0         if (defined $row_list) {
241 0           foreach my $row (@$row_list) {
242 0           my $line = CGI::Listman::line->new ();
243 0           $line->_build_from_listman_data ($row);
244 0           $self->add_line ($line);
245             }
246             }
247              
248 0           $self->{'_loading_list'} = undef;
249             }
250              
251             sub list_contents {
252 0     0 1   my $self = shift;
253              
254 0           my $contents_ref = undef;
255 0 0         if (defined $self->{'list'}) {
256 0           my @filt_contents;
257 0           my $old_cref = $self->{'list'};
258 0           foreach my $line (@$old_cref) {
259             push @filt_contents, $line
260 0 0         if (!$line->{'_deleted'});
261             }
262 0           $contents_ref = \@filt_contents;
263             } else {
264 0           $self->load_lines ();
265 0           $contents_ref = $self->{'list'};
266             }
267              
268 0           return $contents_ref;
269             }
270              
271             # Check the validity of received parameters and returns two refs against
272             # the missing mandatory values and the unknown fields.
273             sub check_params {
274 0     0 0   my ($self, $param_hash_ref) = @_;
275              
276 0           my $dictionary = $self->dictionary ();
277              
278 0           my @missing;
279             my @unknown;
280              
281 0           foreach my $key (keys %$param_hash_ref) {
282 0           my $term = $dictionary->get_term ($key);
283 0 0         push @unknown, $key
284             unless (defined $term);
285             }
286              
287 0           my $dict_terms = $dictionary->terms ();
288              
289 0           foreach my $term (@$dict_terms) {
290 0           my $key = $term->{'key'};
291             push @missing, $term->definition_or_key ()
292             if ($term->{'mandatory'}
293             && (!defined $param_hash_ref->{$key}
294 0 0 0       || $param_hash_ref->{$key} eq ''));
      0        
295             }
296              
297 0           return (\@missing, \@unknown);
298             }
299              
300             sub _prepare_record {
301 0     0     my ($self, $line) = @_;
302              
303 0           my $fields_ref = $line->line_fields ();
304 0           my @records;
305 0           push @records, ($line->{'timestamp'}, $line->{'seen'}, $line->{'exported'});
306 0           push @records, @$fields_ref;
307              
308 0           my $record_line = "'".$line->{'number'}."'";
309 0           foreach my $record (@records) {
310 0 0         $record = '' unless (defined $record);
311 0           $record_line .= ", '".$record."'";
312             }
313              
314             # if we don't untaint $record_line, we get a stange error regarding
315             # DBD::SQL::Statement::HASH_ref...
316 0           $record_line =~ m/(.*)/;
317 0           $record_line = $1;
318              
319 0           return $record_line;
320             }
321              
322             sub commit {
323 0     0 1   my $self = shift;
324              
325             die "Commit again?\n"
326 0 0         if (defined $self->{'_commit'});
327              
328 0 0         if (defined $self->{'list'}) {
329 0           $self->_db_connect ();
330 0           my $dbh = $self->{'_db_connection'};
331 0           my $list_ref = $self->{'list'};
332 0           foreach my $line (@$list_ref) {
333 0 0         if ($line->{'_updated'}) {
334 0 0 0       next if ($line->{'_deleted'} && $line->{'_new_line'});
335 0 0         if ($line->{'_deleted'}) {
    0          
336             $dbh->do ("DELETE FROM ".$self->{'table_name'}.
337             " WHERE number = ".$line->{'number'})
338             or die "An DBI error occured while deleting line "
339 0 0         .$line->{'number'}." from ".$self->{'table_name'}
340             .":\n".$dbh->errstr;
341             } elsif ($line->{'_new_line'}) {
342             $line->{'timestamp'} = time ()
343 0 0         unless ($line->{'timestamp'});
344 0           my $record = $self->_prepare_record ($line);
345             my $sth = $dbh->do ("INSERT INTO ".$self->{'table_name'}.
346             " VALUES (".$record.")")
347             or die "An DBI error occured while inserting...\n".$record.
348 0 0         "... into ".$self->{'table_name'}.":\n".$dbh->errstr;
349             } else {
350             $dbh->do ("DELETE FROM ".$self->{'table_name'}.
351             " WHERE number = ".$line->{'number'})
352             or die "An DBI error occured while deleting line "
353 0 0         .$line->{'number'}." from ".$self->{'table_name'}
354             .":\n".$dbh->errstr;
355 0           my $record = $self->_prepare_record ($line);
356             my $sth = $dbh->do ("INSERT INTO ".$self->{'table_name'}.
357             " VALUES (".$record.")")
358             or die "An DBI error occured while inserting...\n".$record.
359 0 0         "... into ".$self->{'table_name'}.":\n".$dbh->errstr;
360             }
361             }
362             }
363 0           $dbh->disconnect ();
364             }
365              
366 0           $self->{'_commit'} = 1;
367             }
368              
369             sub delete_line {
370 0     0 1   my ($self, $line) = @_;
371              
372             die "Cannot delete a line with number equal to 0.\n"
373 0 0         unless ($line->{'number'});
374              
375 0           my $list_ref = $self->{'list'};
376 0 0         die "List empty.\n" unless (defined $list_ref);
377              
378             # delete the line from the list in memory...
379 0           my $count;
380 0           for ($count = 0; $count < @$list_ref; $count++) {
381 0 0         if ($list_ref->[$count] == $line) {
382 0           $line->{'_updated'} = 1;
383 0           $line->{'_deleted'} = 1;
384 0           last;
385             }
386             }
387              
388 0 0         die "Line not found in list."
389             if ($count == @$list_ref);
390             }
391              
392             sub delete_selection {
393 0     0 1   my ($self, $selection) = @_;
394              
395 0           my $list_ref = $selection->{'list'};
396 0 0         die "Selection is empty.\n" unless ($list_ref);
397 0           foreach my $line (@$list_ref) {
398 0           $self->delete_line ($line);
399             }
400             }
401              
402              
403             package CGI::Listman::line;
404              
405 1     1   6 use strict;
  1         2  
  1         409  
406              
407             # line format: (number, timestamp, seen, exported, fields...)
408             sub new {
409 0     0     my $class = shift;
410              
411 0           my $self = {};
412 0           $self->{'number'} = 0;
413 0           $self->{'timestamp'} = 0;
414 0           $self->{'seen'} = 0;
415 0           $self->{'exported'} = 0;
416 0           $self->{'data'} = shift;
417              
418 0           $self->{'_updated'} = 1;
419 0           $self->{'_new_line'} = 1;
420 0           $self->{'_deleted'} = 0;
421              
422 0           bless $self, $class;
423             }
424              
425             sub mark_seen {
426 0     0     my $self = shift;
427              
428 0           $self->{'seen'} = 1;
429 0           $self->{'_updated'} = 1;
430             }
431              
432             sub mark_exported {
433 0     0     my $self = shift;
434              
435 0           $self->{'exported'} = 1;
436 0           $self->{'_updated'} = 1;
437             }
438              
439             sub number {
440 0     0     my $self = shift;
441              
442 0           return $self->{'number'};
443             }
444              
445             sub set_fields {
446 0     0     my ($self, $fields_ref) = @_;
447              
448             die "Fields already defined for line.\n"
449 0 0         if (defined $self->{'data'});
450              
451 0           $self->{'data'} = $fields_ref;
452 0           $self->{'_updated'} = 1;
453             }
454              
455             sub update_fields {
456 0     0     my ($self, $fields_ref) = @_;
457              
458             delete $self->{'data'}
459 0 0         if (defined $self->{'data'});
460              
461 0           $self->{'data'} = $fields_ref;
462 0           $self->{'_updated'} = 1;
463             }
464              
465             sub line_fields {
466 0     0     my $self = shift;
467              
468 0           return $self->{'data'};
469             }
470              
471             # internals only
472             sub _build_from_listman_data {
473 0     0     my ($self, $listman_data_ref) = @_;
474              
475 0           my @backend_data = @$listman_data_ref;
476              
477 0           my $number = shift @backend_data;
478 0           $number =~ m/^([0-9]*)$/;
479 0 0         $number = $1 or die 'Wrong number ("'.$number
480             .'") containing non-digit characters'."\n";
481              
482 0           $self->{'number'} = $number;
483 0           $self->{'timestamp'} = shift @backend_data;
484 0           $self->{'seen'} = shift @backend_data;
485 0           $self->{'exported'} = shift @backend_data;
486 0           $self->{'data'} = \@backend_data;
487              
488 0           $self->{'_updated'} = 0;
489 0           $self->{'_new_line'} = 0;
490             }
491              
492              
493             package CGI::Listman::exporter;
494              
495 1     1   6 use strict;
  1         2  
  1         18  
496 1     1   1019 use Text::CSV_XS;
  1         16919  
  1         538  
497              
498             sub new {
499 0     0     my $class = shift;
500              
501 0           my $self = {};
502              
503 0           my @lines;
504 0           $self->{'file_name'} = shift;
505 0   0       $self->{'separator'} = shift || ',';
506 0           $self->{'lines'} = \@lines;
507 0           $self->{'_csv'} = Text::CSV_XS->new ({sep_char => $self->{'separator'},
508             binary => 1});
509 0           $self->{'_file_read'} = 0;
510              
511 0           bless $self, $class;
512 0 0         $self->_read_file () if (defined $self->{'file_name'});
513              
514 0           return $self;
515             }
516              
517             sub set_file_name {
518 0     0     my ($self, $file_name) = @_;
519              
520             die "A file name is already defined for this instance"
521             ." of CGI::Listman::exporter.\n"
522 0 0         if (defined $self->{'file_name'});
523 0           $self->{'file_name'} = $file_name;
524 0           $self->_read_file ();
525             }
526              
527             sub set_separator {
528 0     0     my ($self, $sep) = @_;
529              
530 0           $self->{'separator'} = $sep;
531             }
532              
533             sub add_line {
534 0     0     my ($self, $line) = @_;
535              
536 0           my $csv = $self->{'_csv'};
537              
538 0           my $data_ref = $line->{'data'};
539 0           my @columns = @$data_ref;
540 0           $csv->combine (@columns);
541 0           my $csv_line = $csv->string ();
542 0           my $lines_ref = $self->{'lines'};
543 0           push @$lines_ref, $csv_line;
544 0           $line->mark_exported ();
545             }
546              
547             sub add_selection {
548 0     0     my ($self, $selection) = @_;
549              
550 0           my $sel_list_ref = $selection->{'list'};
551 0           foreach my $line (@$sel_list_ref) {
552 0           $self->add_line ($line);
553             }
554             }
555              
556             sub file_contents {
557 0     0     my $self = shift;
558              
559 0           my $contents = undef;
560 0           my $lines_ref = $self->{'lines'};
561 0           foreach my $line (@$lines_ref) {
562 0           $contents .= $line."\r\n";
563             }
564              
565 0           return $contents;
566             }
567              
568             sub save_file {
569 0     0     my $self = shift;
570              
571 0           print STDERR "saving to ".$self->{'file_name'}."\n";
572             die "No file to export to.\n"
573 0 0         unless (defined $self->{'file_name'});
574 0           my $contents = $self->file_contents ();
575              
576             open EFOUT, '>'.$self->{'file_name'}
577             or die "Could not open export file (\""
578 0 0         .$self->{'file_name'}."\") for writing.\n";
579 0           print EFOUT $contents;
580 0           close EFOUT;
581             }
582              
583             sub _read_file {
584 0     0     my $self = shift;
585              
586 0 0         if (-f $self->{'file_name'}) {
587             open EFIN, $self->{'file_name'}
588 0 0         or die "Could not open export file ('".$self->{'file_name'}."').\n";
589              
590 0           my $lines_ref = $self->{'lines'};
591 0           while () {
592 0           my $line = $_;
593 0           chomp $line;
594 0           push @$lines_ref, $line;
595             }
596 0           close EFIN;
597              
598 0           $self->{'_file_read'} = 1;
599             }
600             }
601              
602              
603             package CGI::Listman::selection;
604              
605 1     1   8 use strict;
  1         3  
  1         1297  
606              
607             sub new {
608 0     0     my $class = shift;
609              
610 0           my $self = {};
611 0           my @selection_list;
612 0           $self->{'list'} = \@selection_list;
613              
614 0           bless $self, $class;
615             }
616              
617             sub add_line {
618 0     0     my ($self, $line) = @_;
619              
620 0           my $list_ref = $self->{'list'};
621 0           push @$list_ref, $line;
622             }
623              
624             sub add_line_by_number {
625 0     0     my ($self, $listman, $number) = @_;
626              
627 0           my $line = $listman->seek_line_by_num ($number);
628 0 0         die "Line number ".$number." not found.\n"
629             unless (defined $line);
630 0           $self->add_line ($line);
631             }
632              
633             sub add_lines_by_number {
634 0     0     my ($self, $listman, $numbers) = @_;
635              
636 0           foreach my $number (@$numbers) {
637 0           $self->add_line_by_number ($listman, $number);
638             }
639             }
640              
641              
642             package CGI::Listman::dictionary;
643              
644             sub new {
645 0     0     my $class = shift;
646              
647 0           my $self = {};
648 0           $self->{'filename'} = shift;
649              
650 0           $self->{'_terms'} = undef;
651 0           $self->{'_loading'} = 0;
652              
653 0           bless $self, $class;
654             }
655              
656             sub _load {
657 0     0     my $self = shift;
658              
659 0 0         return if $self->{'_loading'};
660              
661 0           $self->{'_loading'} = 1;
662             die "No dictionary filename.\n"
663 0 0         unless (defined $self->{'filename'});
664              
665             open DINF, $self->{'filename'}
666 0 0         or die "Could not open dictionary (\"".$self->{'filename'}."\").\n";
667              
668 0           my @terms;
669 0           while () {
670 0           my $line = $_;
671 0           chomp $line;
672 0           $line =~ m/([^:]*)(:([^:]+)?(:([!]))?)?/;
673              
674 0           my $key = $1;
675 0   0       my $definition = $3 || '';
676 0   0       my $mandatory = (defined $5 && $5 eq '!');
677              
678 0 0         die "Dictionary entry \"".$key."\" is duplicated."
679             if (defined $self->get_term ($key));
680              
681             my $term_object = CGI::Listman::dictionary::term->new ($key,
682             $definition,
683             $mandatory,
684 0           $self->{'count'});
685 0           push @terms, $term_object;
686             }
687 0           close DINF;
688              
689 0           $self->{'_terms'} = \@terms;
690 0           $self->{'_loading'} = 0;
691             }
692              
693             sub add_term {
694 0     0     my ($self, $term) = @_;
695              
696 0           my $terms_ref = $self->terms ();
697 0           push @$terms_ref, $term;
698             }
699              
700             sub get_term {
701 0     0     my ($self, $key) = @_;
702              
703 0           my $terms_ref = $self->terms ();
704              
705 0           my $term_object = undef;
706              
707 0 0         if (defined $terms_ref) {
708 0           foreach my $term (@$terms_ref) {
709 0 0         next if ($term->{'key'} ne $key);
710 0           $term_object = $term;
711             }
712             }
713              
714 0           return $term_object;
715             }
716              
717             sub terms {
718 0     0     my $self = shift;
719              
720 0 0         $self->_load () unless (defined $self->{'_terms'});
721 0           my $terms_ref = $self->{'_terms'};
722              
723 0           return $terms_ref;
724             }
725              
726             sub term_pos_in_list {
727 0     0     my ($self, $term) = @_;
728              
729 0           my $number = 0;
730 0           my $terms_ref = $self->terms ();
731 0           foreach my $comp_term (@$terms_ref) {
732 0 0         last if ($comp_term == $term);
733 0           $number++;
734             }
735              
736 0           return $number;
737             }
738              
739             sub reposition_term {
740 0     0     my ($self, $term, $delta) = @_;
741              
742 0           my $curr_pos = $self->term_pos_in_list ($term);
743 0           my $new_pos = $curr_pos + $delta;
744 0           my $terms_ref = $self->{'_terms'};
745              
746 0 0 0       unless ($new_pos > scalar (@$terms_ref)
      0        
747             || $new_pos < 0
748             || $delta == 0) {
749 0           my @new_terms_list;
750              
751 0           for (my $count = 0; $count < @$terms_ref; $count++) {
752 0 0         if ($delta > 0) {
753 0 0 0       push @new_terms_list, $terms_ref->[$count + 1]
754             if ($count < $new_pos && $count >= $curr_pos);
755             } else {
756 0 0 0       push @new_terms_list, $terms_ref->[$count - 1]
757             if ($count > $new_pos && $count <= $curr_pos);
758             }
759 0 0 0       push @new_terms_list, $terms_ref->[$count]
      0        
      0        
760             if (($count < $new_pos && $count < $curr_pos)
761             || ($count > $new_pos && $count > $curr_pos));
762 0 0         push @new_terms_list, $term
763             if ($count == $new_pos);
764             }
765              
766 0           delete $self->{'_terms'};
767 0           $self->{'_terms'} = \@new_terms_list;
768             }
769             }
770              
771             sub increase_term_pos {
772 0     0     my ($self, $term, $increment) = @_;
773              
774 0 0         $increment = 1 unless (defined $increment);
775              
776 0           $self->reposition_term ($term, $increment);
777             }
778              
779             sub decrease_term_pos {
780 0     0     my ($self, $term, $decrement) = @_;
781              
782 0 0         $decrement = 1 unless (defined $decrement);
783              
784 0           $self->reposition_term ($term, -$decrement);
785             }
786              
787             sub increase_term_pos_by_key {
788 0     0     my ($self, $key, $increment) = @_;
789              
790 0           my $term = $self->get_term ($key);
791 0           $self->increase_term_pos ($term, $increment);
792             }
793              
794             sub decrease_term_pos_by_key {
795 0     0     my ($self, $key, $decrement) = @_;
796              
797 0           my $term = $self->get_term ($key);
798 0           $self->decrease_term_pos ($term, $decrement);
799             }
800              
801             sub save {
802 0     0     my $self = shift;
803              
804             open DOUTF, '>'.$self->{'filename'}
805             or die "Could not open dictionary (\""
806 0 0         .$self->{'filename'}."\" for writing).\n";
807 0           my $terms_ref = $self->{'_terms'};
808 0           foreach my $term (@$terms_ref) {
809 0           my $line = $term->{'key'};
810 0           my $definition = $term->definition ();
811 0 0 0       $line .= ':'.$definition if (defined $definition && $definition ne '');
812 0 0         if ($term->{'mandatory'}) {
813 0 0 0       $line .= (defined $definition && $definition ne '') ? ':!' : '::!';
814             }
815 0           print DOUTF $line."\n";
816             }
817 0           close DOUTF;
818             }
819              
820             package CGI::Listman::dictionary::term;
821              
822             sub new {
823 0     0     my $class = shift;
824              
825 0           my $self = {};
826 0           $self->{'key'} = shift;
827 0           $self->{'_definition'} = shift;
828 0   0       $self->{'mandatory'} = shift || 0;
829              
830 0           bless $self, $class;
831             }
832              
833             sub set_key {
834 0     0     my ($self, $key) = @_;
835              
836 0 0 0       die "Bad key name.\n" unless (defined $key && $key ne '');
837             die 'This term already has a key name ("'.$self->{'key'}."\n"
838 0 0         if (defined $self->{'key'});
839 0           $self->{'key'} = $key;
840             }
841              
842             sub set_definition {
843 0     0     my ($self, $definition) = @_;
844              
845 0 0 0       $definition = undef if (defined $definition
846             && ($definition =~ m/^\s+$/));
847 0           $self->{'_definition'} = $definition;
848             }
849              
850             sub set_mandatory {
851 0     0     my $self = shift;
852              
853 0           $self->{'mandatory'} = 1;
854             }
855              
856             sub definition {
857 0     0     my $self = shift;
858              
859 0           my $definition = $self->{'_definition'};
860              
861 0           return $definition;
862             }
863              
864             sub definition_or_key {
865 0     0     my $self = shift;
866              
867 0   0       my $definition = $self->definition () || $self->{'key'};
868              
869 0           return $definition;
870             }
871              
872             1;
873             __END__