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

Last change on this file since 2260 was 2260, checked in by nanardon, 5 years ago

Allow to set in config monitored attributes

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