source: trunk/LATMOS-Accounts/lib/LATMOS/Accounts/Bases/Objects.pm @ 2393

Last change on this file since 2393 was 2371, checked in by nanardon, 4 years ago

Avoid undef warning

  • Property svn:keywords set to Id Rev
File size: 25.2 KB
Line 
1package LATMOS::Accounts::Bases::Objects;
2
3use 5.010000;
4use strict;
5use warnings;
6
7use overload '""' => 'stringify';
8
9use LATMOS::Accounts::Log;
10use LATMOS::Accounts::Bases::Attributes;
11use LATMOS::Accounts::Utils;
12use Crypt::Cracklib;
13
14our $VERSION = (q$Rev: 2072 $ =~ /^Rev: (\d+) /)[0];
15
16=head1 NAME
17
18LATMOS::Accounts::Bases::Objects - Base class for account objects
19
20=head1 SYNOPSIS
21
22  use LATMOS::Accounts::Bases::Objects;
23  LATMOS::Accounts::Bases::Objects->new($base, $type, $id);
24
25=head1 DESCRIPTION
26
27=head1 FUNCTIONS
28
29=cut
30
31=head2 is_supported
32
33If exists, must return true or false if the object is supported or not
34
35=cut
36
37=head2 list($base)
38
39List object supported by this module existing in base $base
40
41Must be provide by object class
42
43    sub list {
44        my ($class, $base) = @_;
45    }
46
47=cut
48
49=head2 listReal($base)
50
51List object supported by this module existing in base $base
52
53Can be override by base driver. The result must exclude specials object such alias.
54
55=cut
56
57sub listReal {
58    my ($class, $base) = @_;
59    $class->list($base);
60}
61
62=head2 list_from_rev($base, $rev)
63
64List objects create or modified after base revision C<$rev>.
65
66=cut
67
68=head2 new($base, $id)
69
70Create a new object having $id as uid.
71
72=cut
73
74sub new {
75    my ($class, $base, $id, @args) = @_;
76    # So can be call as $class->SUPER::new()
77    bless {
78        _base => $base,
79        _type => lc(($class =~ m/::([^:]*)$/)[0]),
80        _id => $id,
81    }, $class;
82}
83
84=head2 _create($class, $base, $id, %data)
85
86Must create a new object in database.
87
88Is called if underling base does not override create_object
89
90    sub _create(
91        my ($class, $base, $id, %data)
92    }
93
94=cut
95
96=head2 type
97
98Return the type of the object
99
100=cut
101
102sub type {
103    my ($self) = @_;
104    if (ref $self) {
105        return $self->{_type}
106    } else {
107        return lc(($self =~ /::([^:]+)$/)[0]);
108    }
109}
110
111=head2 base
112
113Return the base handle for this object.
114
115=cut
116
117sub base {
118    return $_[0]->{_base}
119}
120
121=head2 id
122
123Must return the unique identifier for this object
124
125=cut
126
127sub id {
128    my ($self) = @_;
129    $self->{_id}
130}
131
132=head2 AclID
133
134Return object for acl check
135
136=cut
137
138sub AclID { $_[0]->id }
139
140=head2 Iid
141
142Return internal id if different from Id
143
144=cut
145
146sub Iid {
147    my ($self) = @_;
148    $self->id
149}
150
151=head2 stringify
152
153Display object as a string
154
155=cut
156
157sub stringify {
158    my ($self) = @_;
159
160    return $self->id
161}
162
163=head2 list_canonical_fields($for)
164
165Object shortcut to get the list of field supported by the object.
166
167=cut
168
169sub list_canonical_fields {
170    my ($self, $for) = @_;
171    $for ||= 'rw';
172    $self->_canonical_fields($for);
173}
174
175=head2 attribute ($attribute)
176
177Return L<LATMOS::Accounts::Bases::Attributes> object for C<$attribute>
178
179=cut
180
181sub attribute {
182    my ($self, $attribute) = @_;
183
184    my $attrinfo;
185    if (! ref $attribute) {
186        $attrinfo = $self->_get_attr_schema(
187            $self->base)->{$attribute}
188        or return;
189        $attrinfo->{name} = $attribute;
190    } else {
191        $attrinfo = $attribute;
192    }
193
194    return LATMOS::Accounts::Bases::Attributes->new(
195        $attrinfo,
196        $self,
197    );
198}   
199
200sub _canonical_fields {
201    my ($class, $base, $for) = @_;
202    $for ||= 'rw';
203    my $info = $base->_get_attr_schema($class->type);
204    my @attrs = map { $base->attribute($class->type, $_) } keys %{$info || {}};
205    @attrs = grep { ! $_->ro } @attrs if($for =~ /w/);
206    @attrs = grep { $_->readable } @attrs if($for =~ /r/);
207    @attrs = grep { !$_->hidden }  @attrs unless($for =~ /a/);
208    map { $_->name } @attrs;
209}
210
211=head2 GetOtypeDef
212
213This function is called to provide sub object definition. Must be overwritten
214per object class when need.
215
216Must return a hashref listing each sub object type and their related key atribute:
217
218    return {
219        addresses => 'user',
220    }
221
222=cut
223
224sub GetOtypeDef {
225    my ($class) = @_;
226
227    return;
228}
229
230=head2 get_field($field)
231
232Return the value for $field, must be provide by data base.
233
234    sub get_field {
235        my ($self, $field)
236    }
237
238=cut
239
240=head2 get_c_field($cfield)
241
242Return the value for canonical field $cfield.
243
244Call driver specific get_field()
245
246=cut
247
248sub get_c_field {
249    my ($self, $cfield) = @_;
250    $self->base->check_acl($self, $cfield, 'r') or do {
251        $self->base->log(LA_DEBUG, "Permission denied to get %s/%s",
252            $self->id, $cfield
253        );
254        return;
255    };
256    return $self->_get_c_field($cfield);
257}
258
259=head2 get_attributes($attr)
260
261Like get_c_field but always return an array
262
263=cut
264
265sub get_attributes {
266    my ($self, $cfield) = @_;
267    my $res = $self->get_c_field($cfield);
268    if ($res) {
269        return(ref $res ? @{$res} : $res);
270    } else {
271        return;
272    }
273}
274
275sub _get_attributes {
276    my ($self, $cfield) = @_;
277    my $res = $self->_get_c_field($cfield);
278    if ($res) {
279        return(ref $res ? @{$res} : ($res));
280    } else {
281        return;
282    }
283}
284
285sub _get_c_field {
286    my ($self, $cfield) = @_;
287    my $attribute = $self->attribute($cfield) or do {
288        $self->base->log(LA_WARN, "Unknow attribute $cfield");
289        return;
290    };
291    $attribute->readable or do {
292        $self->base->log(LA_WARN, "Attribute $cfield is not readable");
293        return;
294    };
295    return $attribute->get; 
296}
297
298=head2 GetAttributeValue($cfield)
299
300Return the value to exposed to other base
301
302=cut
303
304sub GetAttributeValue {
305    my ($self, $cfield) = @_;
306
307    return $self->get_c_field($cfield);
308}
309
310
311=head2 queryformat ($fmt)
312
313Return formated string according C<$fmt>
314
315=cut
316
317sub queryformat {
318    my ($self, $fmt) = @_;
319    $fmt ||= ''; # avoid undef
320    $fmt =~ s/\\n/\n/g;
321    $fmt =~ s/\\t/\t/g;
322
323    my $old;
324    do {
325        $old = $fmt;
326        $fmt =~ s&(?:%\{([\?!]+)?([^:}%]*)(?::([^}%]*))?\})&
327            my $op = $1;
328            my $attr = $2;
329            my $val = '';
330            my $modifier = $3 || '';
331           
332            if ($attr =~ /^(\w+)\((.*)\)$/) {
333                $val = $self->base->QFunc($1, $2);
334            } else {
335                $val = $self->get_c_field($2);
336            }
337
338            my $res = '';
339            $val = '' unless( defined( $val ) );
340
341            if ($op) {
342                if ($op eq '?') {
343                    $res = $val ? $3 : '';
344                } elsif ($op eq '?!') {
345                    $res = $val ? '' : $3;
346                }
347                if ($res =~ /^(\w+)\((.*)\)$/) {
348                    $res = $self->base->QFunc($1, $2);
349                }
350            } else {
351                $res = $val;
352                foreach (split('\|' , $modifier)) {
353                    /upper/ and do {
354                        $res = uc($res);
355                        next;
356                    };
357                    /ucfirst/ and do {
358                        $res = ucfirst($res);
359                        next;
360                    };
361                    /lower/ and do {
362                        $res = lc($res);
363                        next;
364                    };
365                    /lcfirst/ and do {
366                        $res = lcfirst($res);
367                        next;
368                    };
369                    /ascii/ and do {
370                        $res = LATMOS::Accounts::Utils::to_ascii($res);
371                        next;
372                    };
373                    $res = sprintf('%' . ($modifier || 's'), ref $val ? join(',', @$val) : (defined($val) ? $val : ''))
374                }
375            }
376            $res
377        &egx;
378    } while($old ne $fmt);
379    $fmt;
380}
381
382=head2 set_fields(%data)
383
384Set values for this object. %data is a list or peer field => values.
385
386    sub set_fields {
387        my ($self, %data) = @_;
388    }
389
390=cut
391
392=head2 checkValues ($base, $obj, %attributes)
393
394Allow to pre-check values when object are modified or created
395
396C<$obj> is either the new id at object creation or the object itself on modification.
397
398=cut
399
400sub checkValues {
401    my ($class, $base, $obj, %attributes) = @_;
402
403    return 1;
404}
405
406=head2 check_allowed_values ($attr, $values)
407
408Check if value C<$values> is allowed for attributes C<$attr>
409
410=cut
411
412sub check_allowed_values {
413    my ($self, $attr, $values) = @_;
414    $self->base->check_allowed_values($self->type, $attr, $values);
415}
416
417=head2 attr_allow_values ($attr)
418
419Return allowed for attribute C<$attr>
420
421=cut
422
423sub attr_allow_values {
424    my ($self, $attr) = @_;
425    return $self->base->obj_attr_allowed_values(
426        $self->type,
427        $attr,
428    );
429}
430
431=head2 set_c_fields(%data)
432
433Set values for this object. %data is a list or peer
434canonical field => values. Fields names are translated.
435
436=cut
437
438sub set_c_fields {
439    my ($self, %cdata) = @_;
440    foreach my $cfield (keys %cdata) {
441        $self->base->check_acl($self, $cfield, 'w') or do { 
442            $self->base->log(LA_ERR, "Cannot modified %s/%s: %s",
443                $self->type, $self->id, "permission denied");
444            return;
445        };
446    }
447
448    foreach my $cfield (keys %cdata) {
449        $self->check_allowed_values($cfield, $cdata{$cfield}) or do {
450            $self->base->log(LA_ERR, "Cannot modified %s/%s: %s",
451                $self->type, $self->id, "non authorized value");
452            return;
453        };
454    }
455
456    $self->_set_c_fields(%cdata);
457}
458
459sub _set_c_fields {
460    my ($self, %cdata) = @_;
461    my %data;
462    my $res = 0;
463    foreach my $cfield (keys %cdata) {
464        my $attribute = $self->attribute($cfield) or do {
465            $self->base->log(LA_ERR,
466                "Cannot set unsupported attribute %s to %s (%s)",
467                $cfield, $self->id, $self->type
468            );
469            return;
470        };
471        $attribute->ro and do {
472            $self->base->log(LA_ERR,
473                "Cannot set read-only attribute %s to %s (%s)",
474                $cfield, $self->id, $self->type
475            );
476            return;
477        };
478
479        if (!$attribute->checkinput($cdata{$cfield})) {
480            $self->base->log(LA_ERR,
481                "Value for attribute %s to %s (%s) does not match requirements",
482                $cfield, $self->id, $self->type
483            );
484            return;
485        };
486    }
487
488    if (!$self->checkValues($self->base, $self, %cdata)) {
489        my $last = LATMOS::Accounts::Log::lastmessage(LA_ERR);
490        $self->base->log(LA_ERR,
491            "Cannot update %s (%s): wrong value%s",
492            $self->id, $self->type,
493            ($last ? ": $last" : $last)
494        );
495        return;
496    }
497
498    my %updated = ();
499    foreach my $cfield (keys %cdata) {
500        my $attribute = $self->attribute($cfield) or do {
501            $self->base->log(LA_ERR,
502                "Cannot set unsupported attribute %s to %s (%s)",
503                $cfield, $self->id, $self->type
504            );
505            return;
506        };
507        if ($attribute->set($cdata{$cfield})) {
508            $updated{$cfield} = $attribute->monitored;
509        }
510    }
511
512    if (keys %updated) {
513        $self->PostSetAttribute() or do {
514            $self->base->log(LA_ERR, "PostSetAttribute failed when updating %s/%s",
515                $self->type, $self->id);
516            return;
517        };
518
519        $self->ReportChange('Update', 'Attributes %s have been updated', join(', ', sort keys %updated));
520        foreach (sort keys %updated) {
521            $self->ReportChange('Attributes', '%s set to %s', $_, 
522                (ref $cdata{$_}
523                    ? join(', ', sort @{ $cdata{$_} })
524                    : $cdata{$_}) || '(none)')
525                if ($updated{$_});
526        }
527    }
528    return scalar(keys %updated);
529}
530
531=head2 PostSetAttribute
532
533This function is call to compute data when object is modify.
534
535=cut
536
537sub PostSetAttribute {
538    my ($self) = @_;
539
540    return 1;
541}
542
543=head2 addAttributeValue($attribute, $value)
544
545Add a value to a multivalue attributes
546
547=cut
548
549sub _addAttributeValue {
550    my ($self, $attribute, @values) = @_;
551
552    my @oldvalues = grep { $_ } $self->_get_attributes($attribute);
553    $self->_set_c_fields($attribute => [ @oldvalues, @values ]);
554}
555
556sub addAttributeValue {
557    my ($self, $attribute, @values) = @_;
558
559    my @oldvalues = grep { $_ } $self->_get_attributes($attribute);
560    $self->set_c_fields($attribute => [ @oldvalues, @values ]);
561}
562
563=head2 delAttributeValue($attribute, $value)
564
565Remove a value to a multivalue attributes
566
567=cut
568
569sub _delAttributeValue {
570    my ($self, $attribute, @values) = @_;
571
572    my @oldvalues = grep { $_ } $self->_get_attributes($attribute);
573
574    foreach my $value (@values) {
575        @oldvalues = grep { $_ ne $value } @oldvalues;
576    }
577
578    $self->_set_c_fields($attribute => @oldvalues ? [ @oldvalues, ] : undef );
579}
580
581sub delAttributeValue {
582    my ($self, $attribute, @values) = @_;
583
584    my @oldvalues = grep { $_ } $self->_get_attributes($attribute);
585
586    foreach my $value (@values) {
587        @oldvalues = grep { $_ ne $value } @oldvalues;
588    }
589
590    $self->set_c_fields($attribute => @oldvalues ? [ @oldvalues, ] : undef );
591}
592
593=head2 set_password($password)
594
595Set the password into the database, $password is the clear version
596of the password.
597
598This function store it into userPassword canonical field if supported
599using crypt unix and md5 algorythm (crypt md5), the salt is 8 random
600caracters.
601
602The base driver should override it if another encryption is need.
603
604=cut
605
606sub set_password {
607    my ($self, $clear_pass) = @_;
608    if ($self->base->check_acl($self, 'userPassword', 'w')) {
609        if ($self->_set_password($clear_pass)) {
610             $self->ReportChange('Password', 'user password has changed');
611             return 1;
612        } else {
613            return;
614        }
615    } else {
616        $self->base->log(LA_ERROR, "Permission denied for %s to change its password",
617            $self->id);
618        return;
619    }
620}
621
622sub _set_password {
623    my ($self, $clear_pass) = @_;
624    if (my $attribute = $self->base->attribute($self->type, 'userPassword')) {
625        my $res = $self->set_fields($attribute->iname, $self->base->passCrypt($clear_pass));
626        $self->base->log(LA_NOTICE, 'Mot de passe changé pour %s', $self->id)
627            if($res);
628        return $res;
629    } else {
630        $self->base->log(LA_WARN,
631            "Cannot set password: userPassword attributes is unsupported");
632    }
633}
634
635=head2 check_password ($password)
636
637Check given password is secure using L<Crypt::Cracklib>
638
639=cut
640
641sub check_password {
642    my ( $self, $password ) = @_;
643    my $dictionary = $self->base->config('cracklib_dictionnary');
644
645    if ($password !~ /^[[:ascii:]]*$/) {
646       return "the password must contains ascii characters only";
647    }
648
649    return fascist_check($password, $dictionary);
650}
651
652=head2 InjectCryptPasswd($cryptpasswd)
653
654Inject a password encrypted using standard UNIX method.
655
656=cut
657
658sub InjectCryptPasswd {
659    my ($self, $cryptpasswd) = @_;
660
661    if ($self->can('_InjectCryptPasswd')) {
662        return $self->_InjectCryptPasswd($cryptpasswd);
663    } else {
664        $self->base->log(LA_ERR, 'Injecting unix crypt password is not supported');
665        return;
666    }
667}
668
669=head2 search ($base, @filter)
670
671Search object matching C<@filter>
672
673=cut
674
675sub search {
676    my ($class, $base, @filter) = @_;
677    my @results;
678    my %parsed_filter;
679    while (my $item = shift(@filter)) {
680        # attr=foo => no extra white space !
681        # \W is false, it is possible to have two char
682        my ($attr, $mode, $val) = $item =~ /^(\w+)(?:(\W)(.+))?$/ or next;
683        if (!$mode) {
684            $mode = '~';
685            $val = shift(@filter);
686        }
687        push(
688            @{$parsed_filter{$attr}},
689            {
690                attr => $attr,
691                mode => $mode,
692                val  => $val,
693            }
694        );
695    }
696    foreach my $id ($base->list_objects($class->type)) {
697        my $obj = $base->get_object($class->type, $id);
698        my $match = 1;
699        foreach my $field (keys %parsed_filter) {
700            $base->attribute($class->type, $field) or
701                la_log(LA_WARN, "Unsupported attribute %s", $field);
702            my $tmatch = 0;
703            foreach (@{$parsed_filter{$field}}) {
704                my $value = $_->{val};
705                my $fval = $obj->_get_c_field($field) || '';
706                if ($value eq '*') {
707                    if ($fval ne '') {
708                        $tmatch = 1;
709                        last;
710                    }
711                } elsif ($value eq '!') {
712                    if ($fval eq '') {
713                        $match = 1;
714                        last;
715                    }
716                } elsif ($_->{mode} eq '=') {
717                    if ($fval eq $value) {
718                        $tmatch = 1;
719                        last;
720                    }
721                } elsif($_->{mode} eq '~') {
722                    if ($fval =~ m/\Q$value\E/i) {
723                        $tmatch = 1;
724                        last;
725                    }
726                }
727            }
728            $match = 0 unless($tmatch);
729        }
730        push(@results, $id) if($match);
731    }
732    @results;
733}
734
735=head2 attributes_summary ($base, $attribute)
736
737Return list of values existing in base for C<$attribute>
738
739=cut
740
741sub attributes_summary {
742    my ($class, $base, $attribute) = @_;
743    my $attr = $base->attribute($class->type, $attribute) or do {
744        $base->log(LA_WARN, "Cannot instantiate %s attribute for class %s", $attribute, $class->type);
745        return;
746    };
747    if (!$attr->readable) {
748        $base->log(LA_WARN, l('Attribute %s is not readable', $attribute));
749        return;
750    }
751    if (!$base->check_acl($class->type, $attribute, 'r')) {
752        $base->log(LA_WARN, l('Permission denied to read attribute %s', $attribute));
753        return;
754    }
755    my %values;
756    foreach my $id ($base->list_objects($class->type)) {
757        my $obj = $base->get_object($class->type, $id);
758        my $value = $obj->_get_c_field($attribute);
759        if ($value) {
760            if (ref $value) {
761                foreach (@$value) {
762                    $values{$_} = 1;
763                }
764            } else {
765                $values{$value} = 1;
766            }
767        }
768    }
769    return sort(keys %values);
770}
771
772=head2 attributes_summary_by_object ($base, $attribute)
773
774Return list of peer object <=> values
775
776=cut
777
778sub attributes_summary_by_object {
779    my ($class, $base, $attribute) = @_;
780    my $attr = $base->attribute($class->type, $attribute) or do {
781        $base->log(LA_WARN, "Cannot instantiate %s attribute for class %s", $attribute, $class->type);
782        return;
783    };
784    if (!$attr->readable) {
785        $base->log(LA_WARN, l('Attribute %s is not readable', $attribute));
786        return;
787    }
788    if (!$base->check_acl($class->type, $attribute, 'r')) {
789        $base->log(LA_WARN, l('Permission denied to read attribute %s', $attribute));
790        return;
791    }
792    my %values;
793    foreach my $id ($base->list_objects($class->type)) {
794        my $obj = $base->get_object($class->type, $id);
795        my $value = $obj->_get_c_field($attribute);
796        if ($value) {
797            if (ref $value) {
798                foreach (@$value) {
799                    push(@{ $values{ $id } }, $_);
800                }
801            } else {
802                push(@{ $values{ $id } }, $value);
803            }
804        }
805    }
806    return %values;
807}
808
809=head2 find_next_numeric_id ($base, $field, $min, $max)
810
811Find next free uniq id for attribute C<$field>
812
813=cut
814
815sub find_next_numeric_id {
816    my ($class, $base, $field, $min, $max) = @_;
817    $base->attribute($class->type, $field) or return;
818    $min ||= 
819        $field eq 'uidNumber' ? 500 :
820        $field eq 'gidNumber' ? 500 :
821        1;
822    $max ||= 65635;
823    $base->log(LA_DEBUG, "Trying to find %s in range %d - %d",
824        $field, $min, $max);
825    my %existsid;
826    $base->temp_switch_unexported(sub {
827        foreach ($base->list_objects($class->type)) {
828            my $obj = $base->get_object($class->type, $_) or next;
829            my $id = $obj->_get_c_field($field) or next;
830            $existsid{$id + 0} = 1;
831        }
832    }, 1);
833    $min += 0;
834    $max += 0;
835    for(my $i = $min; $i <= $max; $i++) {
836        $existsid{$i + 0} or do {
837            $base->log(LA_DEBUG, "Next %s found: %d", $field, $i);
838            return $i;
839        };
840    }
841    return;
842}
843
844=head2 ListChildObjects
845
846List dependant objects.
847
848=cut
849
850sub ListChildObjects {
851    my ( $self ) = @_;
852
853    return;
854}
855
856=head2 DataDump($config)
857
858Return a structure about the object
859
860=cut
861
862sub DataDump {
863    my ($self, $config, $base) = @_;
864
865    # {
866    #   base => # base latmoslocal: object address/thauvin-GY
867    #   otype => 'otype',
868    #   id => 'name',
869    #   roAttrs => []
870    #   Attrs => {
871    #       'Attr' => [],
872    #   }
873    #   subObjs => {
874    #       'otype' => [],
875    #   }
876    # }
877
878
879    $config->{level} ||= 0;
880    my $otype = $self->type;
881    $base ||= $self->base;
882    my $dump = {
883        otype => $otype,
884        id => ref $self ? $self->id : 'N/A',
885    };
886    if (ref $self) {
887        $dump->{base} = $base->label;
888    }
889
890    my %roAttrs = ();
891    foreach my $attr (sort { $a cmp $b } $base->list_canonical_fields($otype,
892        $config->{only_rw} ? 'rw' : 'r')) {
893        my $oattr = ref $self ? $self->attribute($attr) : $base->attribute($otype, $attr);
894        next if ($oattr->hidden);
895
896        if (ref $self) {
897            my $val = $self->get_c_field($attr);
898            if ($val || $config->{empty_attr}) {
899                my @vals = ref $val ? @{ $val } : $val;
900                $dump->{Attrs}{$attr} = \@vals;
901                $roAttrs{ $attr } = 1 if ($oattr->ro);
902            }
903        } else {
904            $dump->{Attrs}{$attr} = undef;
905            $roAttrs{ $attr } = 1 if ($oattr->ro);
906        }
907
908        $dump->{roAttrs} = [ sort keys %roAttrs ]
909            unless($config->{noSchema});
910    }
911
912    if ($config->{cb}) {
913        $config->{cb}->($config, $dump);
914    }
915
916    my $SubOtype = undef;
917
918    if (@{ $config->{SubOtype} || []}) {
919        $SubOtype = { map { $_ => 1 } @{ $config->{SubOtype} } };
920    }
921
922    if (ref $self && $config->{recur}) {
923          my %subobj = $self->ListChildObjects;
924          foreach my $otype (sort keys %subobj) {
925              if ($SubOtype) {
926                  $SubOtype->{$otype} or next;
927              }
928              foreach my $oname (sort @{ $subobj{$otype} }) {
929                  my $obj = $self->base->get_object($otype, $oname) or next;
930                  push(@{ $dump->{subObjs}{$otype} }, $obj->DataDump({ %{$config || {}}, recur => $config->{recur}, level => $config->{level} + 2 }));
931              }
932          }
933    }
934
935    return $dump;
936}
937
938=head2 text_dump ($handle, $config, $base)
939
940Dump object into C<$handle>
941
942=cut
943
944sub text_dump {
945    my ($self, $handle, $config, $base) = @_;
946    print $handle $self->dump($config, $base);
947    return 1;
948}
949
950=head2 dump
951
952Return dump for this object
953
954=cut
955
956sub dump {
957    my ($self, $InitConfig, $base) = @_;
958
959    $InitConfig->{level} ||= 0;
960    $base ||= $self->base;
961    my $dump = '';
962
963    $InitConfig->{cb} = sub {
964        my ( $config, $Dump ) = @_;
965
966        if ($config->{level}) {
967            $dump .= "\n";
968        }
969
970        if (ref $self) {
971            $dump .= sprintf "%s# base %s: object %s/%s\n",
972                ' ' x $config->{level},
973                $Dump->{base}, $Dump->{otype}, $Dump->{id};
974        }
975        $dump .= sprintf(
976            "%s# %s\n",
977            ' ' x $config->{level},
978            scalar(localtime)
979        );
980
981        my %roAttrs = map { $_ => 1 } @{ $Dump->{roAttrs} || [] };
982
983        foreach my $attr (sort { $a cmp $b } sort keys %{ $Dump->{Attrs} || {} }) {
984            my $val = $Dump->{Attrs}{$attr};
985            my $oattr = $base->attribute($Dump->{otype}, $attr);
986            if ($val) {
987                if (my @allowed = $base->obj_attr_allowed_values($Dump->{otype}, $attr)) {
988                    $dump .= sprintf("%s# %s must be%s: %s\n",
989                        ' ' x $config->{level},
990                        $attr,
991                        ($oattr->mandatory ? '' : ' empty or either'),
992                        join(', ', @allowed)
993                    );
994                }
995
996                foreach (@$val) {
997                    $_ ||= '';
998                    s/\r?\n/\\n/g;
999                    $dump .= sprintf("%s%s%s%s:%s\n",
1000                        ' ' x $config->{level},
1001                        $roAttrs{$attr} ? '# (ro) ' : '',
1002                        $config->{level} ? $Dump->{otype} . '[' . $Dump->{id} . '].' : '',
1003                        $attr, $_ ? " $_" : '');
1004                }
1005            } elsif ( $config->{empty_attr} || ! ref $self) {
1006                if (my @allowed = $base->obj_attr_allowed_values($Dump->{otype}, $attr)) {
1007                    $dump .= sprintf("%s# %s must be%s: %s\n",
1008                        ' ' x $config->{level},
1009                        $attr,
1010                        ($oattr->mandatory ? '' : ' empty or either'),
1011                        join(', ', @allowed)
1012                    );
1013                }
1014                $dump .= sprintf("%s%s%s%s:\n",
1015                    ' ' x $config->{level},
1016                    $roAttrs{$attr} ? '# (ro) ' : '',
1017                    $config->{level} ? $Dump->{otype} . '[' . $Dump->{id} . '].' : '',
1018                    $attr);
1019            }
1020        }
1021    };
1022
1023    $self->DataDump($InitConfig, $base);
1024
1025    return $dump;
1026}
1027
1028=head2 ReportChange($changetype, $message, @args)
1029
1030Possible per database way to log changes
1031
1032=cut
1033
1034sub ReportChange {
1035    my ($self, $changetype, $message, @args) = @_;
1036
1037    $self->base->ReportChange(
1038        $self->type,
1039        $self->id,
1040        $self->Iid,
1041        $changetype, $message, @args
1042    )
1043}
1044
10451;
1046
1047__END__
1048
1049
1050=head1 SEE ALSO
1051
1052L<LATMOS::Accounts::Bases>
1053
1054=head1 AUTHOR
1055
1056Thauvin Olivier, E<lt>olivier.thauvin.ipsl.fr@localdomainE<gt>
1057
1058=head1 COPYRIGHT AND LICENSE
1059
1060Copyright (C) 2009 by Thauvin Olivier
1061
1062This library is free software; you can redistribute it and/or modify
1063it under the same terms as Perl itself, either Perl version 5.10.0 or,
1064at your option, any later version of Perl 5 you may have available.
1065
1066=cut
Note: See TracBrowser for help on using the repository browser.