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

Last change on this file was 2499, checked in by nanardon, 2 years ago

Typo

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