source: trunk/LATMOS-Accounts/lib/LATMOS/Accounts/Bases/Attributes.pm @ 2136

Last change on this file since 2136 was 2048, checked in by nanardon, 7 years ago

Fix: checkinput with no value

  • Property svn:keywords set to Id
File size: 10.9 KB
Line 
1package LATMOS::Accounts::Bases::Attributes;
2
3# $Id$
4
5use strict;
6use warnings;
7use LATMOS::Accounts::Log;
8
9use Date::Parse;
10use DateTime;
11use DateTime::TimeZone;
12
13
14=head1 NAME
15
16    LATMOS::Accounts::Bases::Attributes - Object to handle attribute schema
17
18=head1 FUNCTIONS
19
20=head2 new($attributes, $base_or_object, $maybe_otype)
21
22Instanciate a new Attributes object.
23
24=cut
25
26sub new {
27    my ($class, $attributes, $base_or_object, $maybe_otype) = @_;
28   
29    my ($object, $base, $otype) =
30    $base_or_object->isa('LATMOS::Accounts::Bases::Objects')
31        ? ($base_or_object, $base_or_object->base, $base_or_object->type)
32        : (undef, $base_or_object, $maybe_otype);
33
34    if (ref $attributes) {
35        $attributes->{_base} = $base;
36        $attributes->{_object} = $object;
37        $attributes->{_name} = $attributes->{name};
38        $attributes->{_otype} = $otype;
39        return bless($attributes, $class);
40    } else {
41        my $attr_info = $base->get_attr_schema($otype, $attributes) or return;
42
43        $attr_info->{_base} = $base;
44        $attr_info->{_object} = $object;
45        $attr_info->{_name} = $attributes;
46        $attr_info->{_otype} = $otype;
47
48        return bless($attr_info, $class);
49    }
50}
51
52=head2 base
53
54Return base handle
55
56=cut
57
58sub base  { $_[0]->{_base}  }
59
60=head2 name
61
62Return attribute name
63
64=cut
65
66sub name  { $_[0]->{_name}  }
67
68=head2 otype
69
70Return object type for this attribute
71
72=cut
73
74
75sub otype { $_[0]->{_otype} }
76
77=head2 mandatory
78
79Return true if attribute is mandatory
80
81=cut
82
83sub mandatory { $_[0]->{mandatory} || 0 }
84
85=head2 object
86
87Return handle object if any
88
89=cut
90
91sub object { $_[0]->{_object} }
92
93=head2 reference
94
95A object type this attribute refer to
96
97=cut
98
99sub reference {
100    my ($self) = @_;
101    if ($self->{reference} &&
102        $self->base->is_supported_object($self->{reference})) {
103        return $self->{reference};
104    } else {
105        return;
106    }
107}
108
109=head2 iname
110
111Return internal name of attributes
112
113=cut
114
115sub iname { $_[0]->{iname} || $_[0]->name }
116
117=head2 label
118
119Return the label to display
120
121=cut
122
123sub label { $_[0]->{label} || $_[0]->{_name} }
124
125=head2 has_values_list
126
127Return true if the attribute have a fixed list of accepted values
128
129=cut
130
131sub has_values_list {
132    my ($self) = @_;
133    if ($self->base->obj_attr_allowed_values(
134        $self->otype,
135        $self->name) ||
136        $self->{can_values} ||
137        $self->reference) {
138        return 1;
139    } else {
140        return 0;
141    }
142}
143
144=head2 can_values
145
146Return possible value allowed by this attribute
147
148=cut
149
150sub can_values {
151    my ($self) = @_;
152    if (my @values = $self->base->obj_attr_allowed_values(
153            $self->otype,
154            $self->name)) {
155        return @values;
156    } elsif ($self->{can_values}) {
157        if (ref $self->{can_values} eq 'ARRAY') {
158            return @{$self->{can_values}};
159        } elsif (ref $self->{can_values} eq 'CODE') {
160            $self->{can_values}->($self, $self->object);
161        } else {
162            return;
163        }
164    } elsif (my $ref = $self->reference) {
165        return $self->base->list_objects($ref);
166    } else { return }
167}
168
169=head2 display ($value)
170
171Return the well formated value according attribute
172
173=cut
174
175sub display {
176    my ($self, $value) = @_;
177    if ($self->{display}) {
178        return $self->{display}->($self, $value);
179    } else {
180        return $value;
181    }
182}
183
184=head2 input ($value)
185
186Return well formated single value for insert into base
187
188=cut
189
190sub input {
191    my ($self, $value) = @_;
192    if ($self->{input}) {
193        if (!defined($value)) { return }
194        return $self->{input}->($value);
195    } elsif ($self->real_form_type =~ /^(DATE|DATETIME)$/) {
196        if (!$value) {
197            return;
198        } else {
199            $value =~ s:^(\d+)/(\d+)/(\d+):$2/$1/$3:;
200            my $epoch = str2time($value);
201            return $value if (!defined($epoch));
202            my $dt = DateTime->from_epoch(epoch => str2time($value));
203            $dt->set_time_zone( DateTime::TimeZone->new( name => 'local' ) );
204            if ($self->real_form_type eq 'DATE') {
205                return $dt->ymd('-');
206            } else {
207                return $dt->ymd('-') . ' ' . $dt->hms(':');
208            }
209        }
210    } else {
211        return $value;
212    }
213}
214
215=head2 checkinputformat ($value)
216
217Check input value format, return false on error
218
219=cut
220
221sub checkinputformat {
222    my ($self, $value) = @_;
223
224    if ($self->{checkinputformat}) {
225        if (!$self->{checkinputformat}->($value)) {
226           return;
227        }
228    }
229
230    return 1;
231}
232
233=head2 checkinput ($value)
234
235Check input value, return false on error
236
237=cut
238
239sub checkinput {
240    my ($self, $values) = @_;
241
242    foreach my $value (grep { $_ } (ref $values ? @{ $values } : $values)) {
243        if (!$self->checkinputformat($value)) {
244            $self->base->log(LA_ERR, "Wrong format for  %s/%s: %s",
245               $self->name,
246               $self->{_otype},
247               $value
248            );
249            return;
250        }
251    }
252
253    if ($self->mandatory &&
254        (!(defined($values)) || $values eq '')) {
255        $self->base->log(LA_ERR,
256            "%s attribute cannot be empty for object %s",
257            $self->name,
258            $self->{_otype},
259        );
260        return;
261    }
262
263    if (ref $values eq 'ARRAY' && ! $self->multiple) {
264        $self->base->log(LA_WARN, 'Attribute %s is not multi valuesd', $self->name);
265        # TODO: really return an error
266        # return;
267    }
268
269    if (defined($values) && $self->{checkinput}) {
270        foreach my $val (ref $values ? @{ $values } : $values) {
271            if (!$self->{checkinput}->($val)) {
272                return;
273            }
274        }
275        return 1;
276    }
277
278    if ($self->has_values_list && $values) {
279        my @possible = $self->can_values;
280        foreach my $value (ref $values ? @{ $values } : $values) {
281            if (! grep { $value eq $_ } @possible) {
282                $self->base->log(LA_ERR,
283                    "%s attribute cannot have `%s' as value for object %s/%s",
284                    $self->name,
285                    $value,
286                    $self->object->type,
287                    $self->object->id,
288                ) if ($self->object);
289                $self->base->log(LA_DEBUG, "Possible value for %s: %s",
290                    $self->name,
291                    join(', ', @possible)
292                );
293                return;
294            }
295        }
296    }
297
298
299    return 1;
300}
301
302=head2 readable
303
304Return true if attribute can be read
305
306=cut
307
308sub readable {
309    my ($self) = @_;
310    if (ref $self->{readable} eq 'CODE') {
311        return $self->{readable}->($self->object) || 0;
312    } else {
313        return defined($_[0]->{readable}) ? $_[0]->{readable} : 1;
314    }
315}
316
317=head2 ro
318
319Return true if the attribute cannot be write by design
320
321=cut
322
323sub ro {
324    my ($self) = @_;
325    if (ref $self->{ro} eq 'CODE') {
326        return $self->{ro}->($self->object) || 0;
327    } else {
328        return $_[0]->{ro} || 0 
329    }
330}
331
332=head2 readonly
333
334Return true if attribute cannot be read according acls or attributes state
335
336=cut
337
338sub readonly { 
339    my ($self) = @_;
340    return 1 if ($self->ro);
341   
342    return ! $self->check_acl('w');
343}
344
345=head2 check_acl ($mode)
346
347Return true is access to C<$mode> is granted
348
349=cut
350
351sub check_acl {
352    my ($self, $mode) = @_;
353
354    return 1 if ($self->{_noacl});
355
356    return $self->base->check_acl($self->object
357        ? ($self->object, $self->name, $mode)
358        : ($self->otype, '@CREATE', $mode));
359}
360
361=head2 real_form_type
362
363Return the way the fields must be show in GUI.
364For each type option maybe given by form_option
365
366=head3 LABEL
367
368=over 4
369
370=item length
371
372The length to use to show the attribute
373
374=back
375
376=head3 TEXT
377
378=head3 TEXTAREA
379
380=head3 DATE
381
382=head3 LIST
383
384=head3 CHECKBOX
385
386=over 4
387
388=item rawvalue
389
390The real value of the attribute must be show
391
392=back
393
394=cut
395
396sub real_form_type { $_[0]->{formtype} || 'TEXT' }
397
398=head2 form_type
399
400Return the way the attribute must be displayed
401
402=cut
403
404sub form_type {
405    $_[0]->readonly ? 'LABEL' :
406    $_[0]->{formtype} ? $_[0]->{formtype} :
407    $_[0]->has_values_list ? 'LIST' :
408    $_[0]->real_form_type
409}
410
411=head2 form_option ($option)
412
413Return the form option C<$option>
414
415=cut
416
417sub form_option {
418    my ($self, $option) = @_;
419    return $self->{formopts}{$option}
420}
421
422=head2 uniq
423
424Return true is attribute value must be uniq
425
426=cut
427
428sub uniq { $_[0]->{uniq} || 0 }
429
430=head2 multiple
431
432Return true is attribute value can be set several times
433
434=cut
435
436sub multiple { $_[0]->{multiple} || 0 }
437
438=head2 hidden
439
440Return true if attributes must not appear in list by can be query.
441
442=cut
443
444sub hidden { $_[0]->{hide} || 0 }
445
446=head2 delayed
447
448Return true if attribute must be set after object creation during
449synchronisation
450
451=cut
452
453sub delayed { $_[0]->{delayed} || 0 }
454
455=head2 get
456
457Return the value for this attribute
458
459=cut
460
461sub get {
462    my ($self) = @_;
463
464    if (defined($self->object)) {
465        my $res = (ref($self->{get}) eq 'CODE')
466            ? $self->{get}->($self)
467            : $self->object->get_field($self->iname);
468
469        # always return undef if empty
470        # and an array if attribute is multivalued
471        if (!defined($res)) {
472            return undef;
473        } elsif (ref $res) {
474            return $res;
475        } elsif ($self->multiple) {
476            return (ref $res ? $res : [$res])
477        } else {
478            return $res;
479        }
480    } else {
481        return;
482    }
483}
484
485=head2 defaultValue
486
487Return the default static value for this attribute.
488
489=cut
490
491sub defaultValue {
492    my ($self) = @_;
493
494    return $self->base->defaultAttributeValue($self->otype, $self->name)
495}
496
497=head2 getValues
498
499Return value for this attribute, results are always return as an array
500
501=cut
502
503sub getValues {
504    my ($self) = @_;
505
506    my $res = $self->get();
507
508    return ref $res ? grep { $_ } @{ $res } : $res;
509}
510
511
512=head2 set ($values)
513
514Set attribute value to attached object
515
516=cut
517
518sub set {
519    my ($self, $values) = @_;
520
521    my $inputv = ref $values
522        ? [ map { $self->input($_) } @$values ]
523        : $values ? $self->input($values) : undef;
524
525    my $res = ref $self->{set} eq 'CODE'
526        ? $self->{set}->($self, $inputv)
527        : $self->object->set_fields($self->iname, $inputv);
528
529    # If the attribute has a post code, call it
530    if ($res && $self->{post}) {
531        $self->{post}->($self, $inputv);
532    }
533
534    $res
535}
536
537=head2 default_value
538
539Return default value for this attribute
540
541=cut
542
543sub default_value {
544    my ($self) = @_;
545    $self->defaultValue;
546}
547
548=head2 monitored
549
550Return true if the attribute is monitored
551
552=cut
553
554sub monitored {
555    my ($self) = @_;
556
557    if ($self->iname ne $self->name) {
558        my $attr = $self->base->attribute($self->otype, $self->iname) or return;
559        return $attr->monitored;
560    } else {
561        return $self->{monitored} || 0;
562    }
563}
564
5651;
566
567__END__
568
569=head1 SEE ALSO
570
571L<LATMOS::Accounts::Bases>
572
573=head1 AUTHOR
574
575Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
576
577=head1 COPYRIGHT AND LICENSE
578
579Copyright (C) 2012 CNRS SA/CETP/LATMOS
580
581This library is free software; you can redistribute it and/or modify
582it under the same terms as Perl itself, either Perl version 5.10.0 or,
583at your option, any later version of Perl 5 you may have available.
584
585=cut
Note: See TracBrowser for help on using the repository browser.