File Coverage

blib/lib/Ftree/FamilyTreeInfo.pm
Criterion Covered Total %
statement 27 238 11.3
branch 0 56 0.0
condition 0 12 0.0
subroutine 9 21 42.8
pod 0 2 0.0
total 36 329 10.9


line stmt bran cond sub pod time code
1             #######################################################
2             #
3             # Family Tree generation program, v2.0
4             # Written by Ferenc Bodon and Simon Ward, March 2000 (simonward.com)
5             # Copyright (C) 2000 Ferenc Bodon, Simon K Ward
6             #
7             # This program is free software; you can redistribute it and/or
8             # modify it under the terms of the GNU General Public License
9             # as published by the Free Software Foundation; either version 2
10             # of the License, or (at your option) any later version.
11             #
12             # This program is distributed in the hope that it will be useful,
13             # but WITHOUT ANY WARRANTY; without even the implied warranty of
14             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15             # GNU General Public License for more details.
16             #
17             # For a copy of the GNU General Public License, visit
18             # http://www.gnu.org or write to the Free Software Foundation, Inc.,
19             # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20             #
21             #######################################################
22              
23             package Ftree::FamilyTreeInfo;
24              
25 1     1   1369 use strict;
  1         2  
  1         26  
26 1     1   5 use warnings;
  1         3  
  1         25  
27              
28 1     1   5 use Ftree::FamilyTreeBase;
  1         2  
  1         7  
29              
30 1     1   159 use Switch;
  1         1  
  1         8  
31 1     1   254457 use Params::Validate qw(:all);
  1         2  
  1         276  
32 1     1   7 use Sub::Exporter -setup => { exports => [ qw(new main) ] };
  1         2  
  1         15  
33 1     1   461 use Encode qw(decode_utf8);
  1         2  
  1         43  
34 1     1   6 use utf8;
  1         1  
  1         8  
35              
36             our $VERSION = '2.3.31';
37              
38             my $q = new CGI;
39              
40 1     1   69 use base 'Ftree::FamilyTreeBase';
  1         2  
  1         3836  
41             sub new{
42 0     0 0   my $type = shift;
43 0           my $self = $type->SUPER::new(@_);
44             $self->{family_tree_data} =
45 0           Ftree::FamilyTreeDataFactory::getFamilyTree( $self->{settings}{data_source} );
46 0           $self->{pagetype} = undef;
47 0           return $self;
48             }
49              
50             sub main{
51 0     0 0   my ($self) = validate_pos(@_, {type => HASHREF} );
52 0           $self->_process_parameters();
53 0           $self->_password_check();
54              
55 0           switch ($self->{pagetype}) {
  0            
  0            
56 0 0         case "" {$self->_draw_index_page();}
  0            
  0            
  0            
  0            
  0            
  0            
57 0 0         case 'subfamily' {$self->_draw_same_surname_page();}
  0            
  0            
  0            
  0            
  0            
  0            
58 0 0         case 'snames' {$self->_draw_surname_page();}
  0            
  0            
  0            
  0            
  0            
  0            
59 0 0         case 'faces' {$self->_draw_facehall_page();}
  0            
  0            
  0            
  0            
  0            
  0            
60 0 0         case 'emails' {$self->_draw_general_page(\&Person::get_email, 'email', $self->{textGenerator}->{Emails},
  0            
61 0           $self->{textGenerator}->{Total_with_email});}
  0            
  0            
  0            
  0            
62 0 0         case 'hpages' {$self->_draw_general_page(\&Person::get_homepage, 'homepage', $self->{textGenerator}->{Homepages},
  0            
63 0           $self->{textGenerator}->{Total_with_homepage});}
  0            
  0            
  0            
  0            
64 0 0         case 'bdays' {$self->_draw_birthday_page();}
  0            
  0            
  0            
  0            
  0            
  0            
65 0           else { $self->_draw_invalid_page(); }
66 0           }
67            
68 0           return;
69             }
70              
71             #######################################################
72             # processing the parameters (type and passwd)
73             sub _process_parameters {
74 0     0     my ( $self ) = validate_pos(@_, {type => HASHREF} );
75 0           $self->SUPER::_process_parameters();
76 0           $self->{pagetype} = $self->{cgi}->param('type');
77 0 0         $self->{pagetype} = "" unless(defined $self->{pagetype});
78            
79 0           return;
80             }
81              
82             # private functions
83             sub _draw_people_table {
84 0     0     my ($self, $people, $column_number) = validate_pos(@_,
85             {type => HASHREF}, {type => ARRAYREF}, {defaulf => 5});
86 0 0         $column_number = 5 unless defined $column_number; #AAARRRRGGGHHH
87 0           my $nr_of_man = 0;
88 0           my $nr_of_woman= 0;
89 0           print $self->{cgi}->start_table({-cellpadding => '5', -border=>'1', -align=>'center'}),"\n";
90            
91 0           for my $index (0 .. @{$people} - 1 ) {
  0            
92 0 0         print $self->{cgi}->start_Tr() if ( $index % $column_number == 0 );
93 0           my $class = $self->get_cell_class(
94             $people->[$index], \$nr_of_man, \$nr_of_woman);
95 0           print $self->{cgi}->td({-class => $class},
96             $self->aref_tree($people->[$index]->get_name()->get_long_name(), $people->[$index])), "\n";
97 0 0         print $self->{cgi}->end_Tr() if ( ($index % $column_number) == $column_number-1 );
98             }
99 0 0         print $self->{cgi}->end_Tr(),"\n" if ( (@{$people} % $column_number) != 1 );
  0            
100            
101             print $self->{cgi}->end_table(),"\n", $self->{cgi}->br,
102 0           $self->{textGenerator}->summary(scalar(@{$people})),
103             " ($self->{textGenerator}{man}: ", $nr_of_man,
104             ", $self->{textGenerator}{woman}: ", $nr_of_woman,
105 0           ", $self->{textGenerator}{unknown}: ", scalar(@{$people}) - $nr_of_man - $nr_of_woman, ')' ;
  0            
106            
107 0           return;
108             }
109             #########################################################
110             # INDEX PAGE
111             #########################################################
112             sub _draw_index_page {
113 0     0     my ($self, $column_number) = validate_pos(@_,
114             {type => HASHREF}, {type => SCALAR, optional => 1});
115 0           my @people = grep {defined $_->get_name()} $self->{family_tree_data}->get_all_people();
  0            
116 0           @people = sort{$a->get_name()->get_full_name() cmp $b->get_name()->get_full_name()} @people;
  0            
117 0           $self->_toppage($self->{textGenerator}->{members});
118 0           $self->_draw_people_table(\@people, $column_number);
119 0           $self->_endpage();
120            
121 0           return;
122             }
123              
124             #########################################################
125             # Same surname people
126             #########################################################
127             sub _draw_same_surname_page {
128 0     0     my ($self, $column_number) = validate_pos(@_,
129             {type => HASHREF}, {type => SCALAR, optional => 1});
130 0           my $surname = decode_utf8($self->{cgi}->param('surname'));
131 0 0         $surname = "" unless(defined $surname);
132 0 0         my @people = grep {defined $_->get_name() &&
133             $_->get_name()->get_last_name() eq $surname}
134 0           $self->{family_tree_data}->get_all_people();
135            
136 0           @people = sort{$a->get_name()->get_full_name() cmp $b->get_name()->get_full_name()} (@people);
  0            
137 0           $self->_toppage($self->{textGenerator}->People_with_surname($surname));
138 0           $self->_draw_people_table(\@people, $column_number);
139 0           $self->_endpage();
140            
141 0           return;
142             }
143              
144             #########################################################
145             # SURNAME PAGE
146             #########################################################
147             sub _draw_surname_page {
148 0     0     my ($self, $column_number) = validate_pos(@_,
149             {type => HASHREF}, {type => SCALAR, optional => 1});
150 0 0         $column_number = 8 unless( defined $column_number);
151              
152 0           require Set::Scalar;
153 0           my $last_name_set = Set::Scalar->new;
154 0           for my $person ($self->{family_tree_data}->get_all_people()) {
155 0 0 0       $last_name_set->insert($person->get_name()->get_last_name())
156             if((defined $person->get_name()) && (defined $person->get_name()->get_last_name()));
157             }
158            
159 0           $self->_toppage($self->{textGenerator}->{Surnames});
160              
161 0           while ( defined( my $a_last_name = $last_name_set->each ) ) {
162 0           push @{ $self->{nodes} }, $a_last_name;
  0            
163             }
164 0           my @sortednodes = sort @{ $self->{nodes} };
  0            
165              
166 0           print $self->{cgi}->start_table({-cellpadding => '5', -border=>'1', -align=>'center'}),"\n";
167 0           for my $people_count (0 .. $#sortednodes) {
168 0 0         print $self->{cgi}->start_Tr() if ( $people_count % $column_number == 0 );
169 0           print $self->{cgi}->td($self->{cgi}->a({-href => "$self->{treeScript}?type=subfamily&surname=$sortednodes[$people_count]&lang=$self->{lang}"},
170             $sortednodes[$people_count]));
171 0 0         print $self->{cgi}->end_Tr(),"\n" if ( $people_count % $column_number == $column_number - 1 );
172             }
173 0 0         print $self->{cgi}->end_Tr(),"\n" if ( $#sortednodes % $column_number != 0 );
174             print $self->{cgi}->end_table(),"\n", $self->{cgi}->br,
175 0           $self->{textGenerator}->{Total}.' '.$last_name_set->size.' '.$self->{textGenerator}->{people};
176 0           $self->_endpage();
177            
178 0           return;
179             }
180              
181             sub _draw_general_table {
182 0     0     my ($self, $func, $attribute, $people_with_type_r, $text2) = validate_pos(@_,
183             {type => HASHREF}, {type => CODEREF}, {type => SCALAR}, {type => ARRAYREF}, {type => SCALAR});
184 0           my $nr_of_man=0;
185 0           my $nr_of_woman=0;
186            
187             print $self->{cgi}->start_table({-cellpadding => '5', -border=>'1', -align=>'center'}),"\n",
188             $self->{cgi}->Tr($self->{cgi}->th($self->{textGenerator}{photo}), $self->{cgi}->th($self->{textGenerator}{name}),
189 0           $self->{cgi}->th($self->{textGenerator}{$attribute}));
190              
191 0           foreach my $a_person ( @{$people_with_type_r} ) {
  0            
192 0           my $class = $self->get_cell_class(
193             $a_person, \$nr_of_man, \$nr_of_woman);
194             print $self->{cgi}->start_Tr({-class => $class}),
195             $self->{cgi}->td($self->html_img($a_person)),
196            
197             $self->{cgi}->td($self->aref_tree($a_person->get_name()->get_full_name(), $a_person)),
198             $self->{cgi}->td($func->($a_person)),
199 0           $self->{cgi}->end_Tr, "\n";
200             }
201 0           print $self->{cgi}->end_table, "\n", $self->{cgi}->br, $text2, scalar(@{$people_with_type_r}),
202             " ($self->{textGenerator}{man}: ", $nr_of_man,
203             ", $self->{textGenerator}{woman}: ", $nr_of_woman,
204 0           ", $self->{textGenerator}{unknown}: ", scalar(@{$people_with_type_r}) - $nr_of_man - $nr_of_woman, ")" ;
  0            
205              
206 0           return;
207             }
208             #########################################################
209             # GENERAL PAGE
210             #########################################################
211             sub _draw_general_page {
212 0     0     my ($self, $func, $attribute, $title, $text2) = validate_pos(@_,
213             {type => HASHREF}, {type => CODEREF}, {type => SCALAR},
214             {type => SCALAR}, {type => SCALAR});
215            
216 0           my @people_with_type = grep {defined $func->($_)}
217 0           (grep {defined $_->get_name()} $self->{family_tree_data}->get_all_people());
  0            
218 0           @people_with_type = sort{$a->get_name()->get_full_name() cmp $b->get_name()->get_full_name()} (@people_with_type);
  0            
219            
220 0           $self->_toppage($title);
221 0           $self->_draw_general_table($func, $attribute, \@people_with_type, $text2);
222 0           $self->_endpage();
223            
224 0           return;
225             }
226              
227              
228             #########################################################
229             # BIRTHDAYS PAGE
230             #########################################################
231             sub _draw_birthday_page {
232 0     0     my ($self) = validate_pos(@_, {type => HASHREF});
233 0           my $months = $self->{textGenerator}->{months_array};
234 0           my $month = decode_utf8($self->{cgi}->param('month'));
235              
236 0 0         if ( ! defined $month ) {
237 0           $month = (localtime)[4] + 1;
238             }
239             else {
240 0           my $index = 0;
241 0           ++$index while($months->[$index] ne $month);
242 0           $month = $index + 1;
243             }
244            
245            
246             my @people_with_bday = grep {defined $_->get_name() &&
247             defined $_->get_date_of_birth() && !defined $_->get_date_of_death() &&
248 0 0 0       defined $_->get_date_of_birth()->{month} && $_->get_date_of_birth()->{month} == $month}
      0        
      0        
249 0           ($self->{family_tree_data}->get_all_people());
250              
251 0           my $title = $self->{textGenerator}->birthday_reminder($month-1);
252 0           $self->_toppage($title);
253 0           @people_with_bday = sort{$a->get_name()->get_full_name() cmp $b->get_name()->get_full_name()} (@people_with_bday);
  0            
254              
255             $self->_draw_general_table(\&Person::get_date_of_birth, 'date_of_birth', \@people_with_bday,
256 0           $self->{textGenerator}->total_living_with_birthday($month-1));
257              
258             # Add the button for other months
259             print $self->{cgi}->start_form({-action => $self->{treeScript},
260             -method => 'get' }),
261             "\n$self->{textGenerator}->{CheckAnotherMonth}:\n",
262 0           $self->{cgi}->start_Select({-name => 'month',
263             -size => 1}), "\n";
264 0           for my $index (0 .. 11) {
265 0 0         if ( $index == ( $month - 1 ) ) {
266 0           print $self->{cgi}->option({-selected => "selected"}, $months->[$index]), "\n";
267             }
268             else {
269 0           print $self->{cgi}->option( $months->[$index]), "\n";
270             }
271             }
272             print $self->{cgi}->end_Select, "\n", $self->{cgi}->input({-type => 'hidden', -name => "type", -value => "bdays"}), "\n",
273             $self->{cgi}->input({-type => 'hidden', -name => 'password', -value => $self->{settings}{password}}), "\n",
274             $self->{cgi}->input({-type => 'hidden', -name => 'lang', -value => $self->{lang}}), "\n",
275              
276             $self->{cgi}->input({-type => "submit", -value => "$self->{textGenerator}->{Go}"}),
277 0           $self->{cgi}->end_form;
278              
279 0           $self->_endpage();
280            
281 0           return;
282             }
283             #########################################################
284             # Facehall page
285             #########################################################
286             sub _draw_facehall_page {
287 0     0     my ($self) = validate_pos(@_, {type => HASHREF});
288 0           my $column_number = 5;
289            
290 0 0         my @people_with_photo = grep {defined $_->get_name() &&
291             defined $_->get_default_picture()}
292 0           ($self->{family_tree_data}->get_all_people());
293 0           @people_with_photo = sort{ $a->get_name()->get_full_name() cmp
  0            
294             $b->get_name()->get_full_name() } (@people_with_photo);
295            
296 0           $self->_toppage($self->{textGenerator}->{Hall_of_faces});
297              
298 0           my $nr_of_man = 0;
299 0           my $nr_of_woman = 0;
300 0           print $self->{cgi}->start_table({-cellpadding => '7', -align=>'center'}),"\n";
301            
302 0           foreach my $index (0 .. $#people_with_photo) {
303 0 0         print $self->{cgi}->start_Tr,"\n" if ( $index % $column_number == 0 );
304 0           my $class = $self->get_cell_class(
305             $people_with_photo[$index], \$nr_of_man, \$nr_of_woman);
306             print $self->{cgi}->start_td({-class => $class, -align=>'center'}),
307             $self->aref_tree($self->html_img($people_with_photo[$index]), $people_with_photo[$index]), $self->{cgi}->br,
308 0           $people_with_photo[$index]->get_name()->get_full_name(), $self->{cgi}->end_td;
309 0 0         print $self->{cgi}->end_Tr,"\n" if ( $index % $column_number == $column_number-1 );
310             }
311 0 0         print $self->{cgi}->end_Tr,"\n" if ( $#people_with_photo % $column_number != 0 );
312             print $self->{cgi}->end_table,"\n", $self->{cgi}->br,
313 0           $self->{textGenerator}->{Total_with_photo}, scalar(@people_with_photo),
314             " ($self->{textGenerator}{man}: ", $nr_of_man,
315             ", $self->{textGenerator}{woman}: ", $nr_of_woman,
316             ", $self->{textGenerator}{unknown}: ", scalar(@people_with_photo) - $nr_of_man - $nr_of_woman, ")" ;
317 0           print $self->{cgi}->start_table({-cellpadding => '7', -align=>'center'}),"\n";
318 0           print $self->{cgi}->start_Tr;
319 0           print $self->{cgi}->start_td({-align=>'center'});
320 0           print $self->{cgi}->br, " $self->{textGenerator}{Prayer_for_the_living}: ";
321            
322 0           foreach my $index (0 .. $#people_with_photo) {
323 0 0         if ($people_with_photo[$index]->get_is_living()){
324 0           print $self->{cgi}->br,$people_with_photo[$index]->get_name()->get_first_name(),' (',$people_with_photo[$index]->get_name()->get_full_name(),')';
325             }
326             }
327            
328 0           print $self->{cgi}->end_td;
329            
330 0           print $self->{cgi}->start_td({-align=>'center'});
331 0           print $self->{cgi}->br, " $self->{textGenerator}{Prayer_for_the_departed}: ";
332 0           foreach my $index (0 .. $#people_with_photo) {
333 0 0         if (!$people_with_photo[$index]->get_is_living()){
334 0           print $self->{cgi}->br,$people_with_photo[$index]->get_name()->get_first_name(),' (',$people_with_photo[$index]->get_name()->get_full_name(),')';
335             }
336             }
337 0           print $self->{cgi}->end_td;
338            
339 0           print $self->{cgi}->end_Tr,"\n";
340              
341 0           print $self->{cgi}->end_table,"\n", $self->{cgi}->br;
342              
343              
344 0           $self->_endpage();
345            
346 0           return;
347             }
348             #########################################################
349             # INVALID PAGE TYPE ERROR
350             #########################################################
351             sub _draw_invalid_page {
352 0     0     my ($self) = validate_pos(@_, {type => HASHREF});
353 0           $self->_toppage($self->{textGenerator}->{Error});
354              
355             print $self->{textGenerator}->{Invalid_option}, $self->{cgi}->br, "\n",
356 0           $self->{textGenerator}->{Valid_options};
357              
358 0           $self->_endpage();
359 0           exit 1;
360             }
361              
362             1;
363             __END__
364             =encoding utf-8
365              
366             =for stopwords
367              
368             =head1 NAME
369              
370             Ftree - family tree generator
371              
372             =head1 EXAMPLE
373              
374             L<https://still-lowlands-7377.herokuapp.com>
375              
376             =head1 SYNOPSIS
377            
378             installator for Windows 7 32bit
379             L<https://sourceforge.net/projects/family-tree-32/files/latest/download?source=navbar>
380              
381             #If install it
382             cpanm https://cpan.metacpan.org/authors/id/M/MI/MISHIN/FamilyTreeInfo-2.3.16.tar.gz
383              
384             #copy the folder cgi-bin from the distribution
385             cp cgi-bin c:\ftree\cgi-bin
386            
387             #then got to it directory
388             c:\ftree\cgi-bin
389             #and run
390             plackup
391            
392             #HTTP::Server::PSGI: Accepting connections at http://0:5000/
393              
394             #now go to the browser
395             http://127.0.0.1:5000/
396              
397             #and we can see a family tree, and
398             #to his Office just need to edit the file
399             c:\ftree\cgi-bin\tree.xls
400            
401             #or the file with a different name, but then this name must indicate file
402             ftree.config
403             #changing parameter
404             file_name tree.xls
405             #on your
406              
407             #and pictures of relatives should be 3 x 4
408             #and they need to be put in the directory
409             c:\ftree\cgi-bin\pictures
410             #where the name of the picture must be a person id + .jpg
411             #all works!
412              
413             #for Unix you will need to fix option
414              
415             photo_dir c:/ftree/cgi-bin/pictures/
416              
417             #on your
418              
419             =head1 OTHER Guts (you never need to read it)
420              
421             =head1 PACKAGE CONTENTS:
422              
423             readme.txt This file
424             config/PerlSettingsImporter.pm Settings file
425             cgi/ftree.cgi The main perl script
426             cgi/*.pm Other perl modules
427             tree.txt, tree.xls, royal.ged Example family tree data files
428             license.txt The GNU GPL license details
429             changes.txt Change history
430             pictures/*.[gif,png,jpg,tif] The pictures of the relatives
431             graphics/*.gif The system graphic files
432              
433             =head1 OVERVIEW:
434            
435             When I designed the Family Tree Generator, I wanted more than just an online version of a traditional tree. With this software it is possible to draw a tree of ancestors and descendants for any person, showing any number of generations (where the information exists).
436             Most other web-based "trees" are little more than text listings of people.
437              
438             A simple datafile contains details of people and their relationships. All the HTML pages are generated on the fly. This means that the tree is easy to maintain.
439              
440             Note that the tree shows the "genetic" family tree. It contains no information about marriages and adaptation.
441              
442             For a demonstration of this software, visit http://www.ilab.sztaki.hu/~bodon/Simpsons/cgi/ftree.cgi or http://www.ilab.sztaki.hu/~bodon/ftree2/cgi/ftree.cgi.
443              
444             The program is written in Perl.
445             It runs as a CGI program - its output is the HTML of the page that you see.
446             The program reads in the data file, and analyzes the relationships to determine the ancestors, siblings and descendants of the person selected.
447             HTML tables are generated to display these trees, linking in the portrait images where they exist.
448              
449             =head1 INSTALLATION INSTRUCTIONS:
450              
451             1. Set up your web server (apache or IIS) so that it can run Perl scripts (e.g. mod-perl).
452              
453             2. Uncompress and copy the demo package (make sure that you reserve the rights, i.e. files with extension pm, gif, jpg, png, tif, csv, txt, xls must be readable, files with extension cgi and pl must be executable).
454              
455             3. Modify tree.xls so that it contains the details of your family. Tip: Select the second row, click on menu Window and select Freeze Panels. This will freeze the first row and you can see the title of columns.
456             See format description below.
457              
458             4. Update the config/PerlSettingsImporter.pm file (you can specify the administrator's email, homepage, default language etc.).
459              
460             5. Copy the pictures of your family members to the pictures directory.
461              
462             6. That's it.
463             Call the ftree.cgi script with no parameters to get your index page.
464              
465             7. If you are unhappy with the style and colors of the output then point the css_filename entry in PerlSettingsImporter.pm into your stly sheet.
466              
467             =head1 INSTALLATION INSTRUCTIONS FOR XAMPP for Windows 5.6.12:
468              
469             Download I use xampp XAMPP for Windows 5.6.12 (https://www.apachefriends.org/ru/download.html) to install and configure Apache
470            
471             <IfModule alias_module>
472             ScriptAlias /cgi-bin/ "C:/xampp/cgi-bin/ftree/cgi/"
473             </IfModule>
474            
475             <Directory "C:/xampp/cgi-bin/ftree/cgi">
476             AllowOverride All
477             Options None
478             Require all granted
479             </Directory>
480            
481             My shebang in ftree.cgi is #!"c:\Dwimperl\perl\bin\perl.exe" (by Gabor Sabo)
482            
483             copy c:\xampp\cgi-bin\ftree\graphics\
484             to
485             c:\xampp\htdocs\graphics\
486            
487             to correct show images
488            
489             I catch error couldn't create child process: 720002
490             ------------------------
491             It was the first line in the .cgi file that needed to be adapted to Xamp's configuration:
492            
493             #!"c:\xampp\perl\bin\perl.exe"
494             Instead of:
495            
496             #!"c:\perl\bin\perl.exe"
497            
498             https://forum.xojo.com/20697-couldn-t-create-child-process-720002-error-when-deploying-on-wi/0
499             http://open-server.ru/forum/viewtopic.php?f=6&t=1059
500            
501             =head1 NAME OF THE PICTURE:
502            
503             One picture may belong to each person.
504             No image put here and name=id.jpg
505             c:\xampp\cgi-bin\ftree\pictures\
506              
507             =head1 DATAFILE FORMAT:
508              
509             The program can handle excel, csv (txt), gedcom, serialized files and can get data from database. Follow these rules to decide which one to use:
510             1, Use gedcom if you already have your family tree data in a gedcom file and the fields that the program is able to import is sufficient.
511             2, Use the excel format if you just started to build your family tree data.
512             3, Convert your data file into serialized format if the data file contains many people (like some thousand) and you would like to reduce response time and memory need.
513              
514             Data format history:
515             Originally the input file was a csv flat file with semicolon as the separator. It could store 6 fields for each person (name, father name, mother name, email, webpage, date of birth/death). As new fields were required (like gender, place of birth, cemetery, etc.) and the number of optional fields increased from 5 to 22, the csv format turned out to be hard to maintain. Although it is possible to be imported/exported into excel, it would be better to use excel spreadsheets directly. From version 2.2 this is possible. For backward compatibility it is still possible to use csv files. The new fields can be used in csv fields as well. From version 2.3 gedcom files can also be used.
516              
517             We encourage everybody to use the excel format. To convert from the csv format to the excel format, use script script/convertFormat.pl
518              
519             TIP 1.: Maintain your family tree data in excel using the Form option. Select all the columns, then press DATA->Form. It is convenient to add new people or to modify information of existing persons.
520             TIP 2.: Freeze the first line so that header does not disappear when scrolling down.
521              
522             =head1 The excel format:
523              
524             The excel format is quite straightforward based on the example file. Each row (except the header) represents a person. The fields are:
525             * ID: the ID of the person. It can be anything (like 123 or Bart_Simpson), but it should only contain alphanumeric characters and underscore (no whitespace is allowed).
526             * title: like: Dr., Prof.
527             * prefix: like: sir
528             * first name
529             * middle name
530             * last Name
531             * suffix: like: VIII
532             * nickname
533             * father's ID
534             * mother's ID
535             * email
536             * webpage
537             * date of birth: the format is day/month/year, like: 24/3/1977
538             * date of death: the format is day/month/year, like: 24/3/1977
539             * gender: 0 for male, 1 female
540             * is living?: 0 for live 1 for dead
541             * place of birth: the format is: "country" "city". The city part may be omitted. Quotation marks are mandatory.
542             * place of death: the format is: "country" "city". The city part may be omitted. Quotation marks are mandatory.
543             * cemetery: the format is: "country" "city" "cemetery", like: "USA" "Washington D.C." "Arlington National Cemetery"
544             * schools: use comma as separator, like: Harward, MIT
545             * jobs: use comma as separator
546             * work places: use comma as separator
547             * places of living: places separated by comma, like: "USA" "Springfield", "USA" "Connecticut"
548             * general: you would typically write something general about the person.
549             Note, that the extension of an excel data file must be xls.
550              
551             Tip: Select the second row, click on menu Window and select Freeze Panels.
552             This will freeze the first row and you can see the title of columns.
553              
554             =head1 The csv format:
555              
556             Semicolon is the separator. The fields are:
557              
558             1. Full name.
559             Middle names can be included in this field.
560             If more than one person share the same name, a number can be appended (not shown in the displayed output). For example, "Bart Simpson2".
561             2. Father (optional - leave blank if not known). No middle names.
562             3. Mother (optional)
563             4. email address (optional)
564             5. web page (optional)
565             6. Dates, birth-death (both optional).
566             Examples: "17/10/49-24/11/83", "10/69-"
567             Note that the year of birth is not displayed for people who are still alive.
568             7. Gender (0 for male, 1 for female)
569             8. title: like: Dr., Prof.
570             9. prefix: like: sir
571             10. suffix: like: VIII
572             11. is living?: 0 for live 1 for dead
573             12. place of birth: the format is: "country" "city". The city part may be omitted. Quotation marks are mandatory.
574             13. place of death: the format is: "country" "city". The city part may be omitted. Quotation marks are mandatory.
575             14. cemetery: the format is: "country" "city" "cemetery", like: "USA" "Washington D.C." "Arlington National Cemetery"
576             15. schools: use comma as separator, like: Harward,MIT
577             16. jobs: use comma as separator
578             17. work places: use comma as separator
579             18. places of living: places separated by comma, like: "USA" "Springfield", "USA" "Connecticut"
580             19. general: you would typically write something general about the person.
581             Note, that the extension of a csv data file must be either csv or txt. To define the encoding of the file use option encoding in the config file.
582              
583             =head1 Convert from csv (txt) format to excel format:
584              
585             To switch from comma separated value file to excel spreadsheet, do the following:
586             cd ftree2
587             perl ./scripts/convertFormat.pl ./tree.txt ./tree.xls
588             This will generate (overwrite) a tree.xls file.
589              
590             The GEDCOM format:
591             GEDCOM, an acronym for GEnealogical Data COMmunication, is a specification for exchanging genealogical data between different genealogy software. GEDCOM was developed by The Church of Jesus Christ of Latter-day Saints as an aid in their extensive genealogical research. A GEDCOM file is plain text (an obscure text encoding named ANSEL, though often in ASCII in the United States) containing genealogical information about individuals, and data linking these records together. Most genealogy software supports importing from and/or exporting to GEDCOM format.
592              
593             Beside the father, mother relationships, the program handles the following information of a person:
594             1, gender
595             2, date of birth
596             3, date of death
597             4, place of birth (only city and country are extracted)
598             5, place of death (only city and country are extracted)
599             6, cemetery (only cemetery, city and country are extracted)
600             7, email address
601             8, homepage
602              
603             It is possible to switch from GEDCOM to excel (or serialized) format. Use the scripts/convertFormat.pl script. For example
604             cd ftree2
605             perl ./scripts/convertFormat.pl ./tree.ged ./tree.xls
606              
607             The ser format:
608             The drawback of excel, csv and GEDCOM format is that it has to be parsed and processed every time the program runs. It is possible to speed-up the program (and hence reduce response time) and reduce memory usage if you use the serialized format. The serialized format cannot be edited directly. Basically you maintain your family tree data in excel (or in csv or GEDCOM) then create a serialized file using scripts/convertFormat.pl program. If the name of the family tree data is ftree.xls then, the following commands will generate the serialized file:
609              
610             cd ftree2
611             perl ./scripts/convertFormat.pl ./tree.xls ./tree.ser
612              
613             Don't forget to set the data_source to "../tree.ser" in the PerlSettingsImporter.pm file.
614              
615             Note, that the extension of a serialized data file must be ser. Also keep in mind that different versions of perl may produce incompatible serialized versions. It is advised to run the convertFormat.pl script on the same mashine where the webserver runs.
616              
617             =head1 NAME OF THE PICTURE:
618              
619             One picture may belong to each person. The name of the picture file reflects the person it belongs to. The picture file is obtained from the lowercased full name by substituting spaces with underscores and adding the file extension to it. From example from "Ferenc Bodon3" we get "ferenc_bodon3.jpg".
620              
621             =head1 PERFORMANCE ISSUES:
622              
623             This sofware was not designed so that it can handle very large family trees. It can easily cope with few thousands of members, but latency (time till page is generated) grows as the size of the family tree increases.
624             The main bottleneck of performance is that (1.) mod_perl is not used, therefore perl interpreter is starts for every request (2.) family tree is not cached but data file is parsed and tree is built-up for every request (using serialized format helps a little).
625             Since the purpose of this software is to provide a free and simple tool for those who would like to maintain their family tree themself, performance is not the primary concern.
626              
627             =head1 SECURITY ISSUES:
628              
629             The protection provided by password request (set in config file) is quite primitive, i.e. it is easy to break it.
630             Ther are historical reasons for being available. We suggest to use server side protection like .htaccess files in case of apache web servers.
631              
632             =head1 AUTHORS
633              
634             Dr. Ferenc Bodon and Simon Ward and Nikolay Mishin
635             http://www.cs.bme.hu/~bodon/en/index.html
636             http://simonward.com
637              
638             =head1 MAINTAINER
639              
640             Nikolay Mishin
641              
642             =head1 COPYRIGHT
643              
644             Copyright 2015- Dr. Ferenc Bodon and Simon Ward and Nikolay Mishin
645              
646             =head1 LICENSE
647              
648             This library is free software; you can redistribute it and/or modify
649             it under the same terms as Perl itself.
650              
651             =head1 ACKNOWLEDGEMENTS
652              
653             I am in debt to the translators:
654             Csaba Kiss (French)
655             Gergely Kovacs (German),
656             Przemek Swiderski (Polish),
657             Rober Miles (Italian),
658             Lajos Malozsak (Romanian),
659             Vladimir Kangin (Russian)
660              
661             I also would like to thank the feedback/help of (in no particular order) Alex Roitman, Anthony Fletcher,
662             Richard Bos, Sylvia McKenzie and Sean Symes.
663              
664             =head1 SEE ALSO
665              
666             =cut
667