source: trunk/LATMOS-Accounts/lib/LATMOS/Accounts/Bases/Sql/DataRequest.pm @ 1904

Last change on this file since 1904 was 1116, checked in by nanardon, 12 years ago

fix typo Base:: instead Bases:: in module name

  • Property svn:keywords set to Id Rev
File size: 15.0 KB
Line 
1package LATMOS::Accounts::Bases::Sql::DataRequest;
2
3use 5.010000;
4use strict;
5use warnings;
6
7use LATMOS::Accounts::Utils;
8use LATMOS::Accounts::Log;
9use LATMOS::Accounts::Acls::Acl;
10
11our $VERSION = (q$Rev$ =~ /^Rev: (\d+) /)[0];
12
13=head1 NAME
14
15LATMOS::Accounts::Bases::Sql::DataRequest - Object modification request
16
17=head1 DESCRIPTION
18
19Object to handle request forms in Link::Accounts system
20
21=cut
22
23=head1 FUNCTIONS
24
25=head2 new($accreq, $id)
26
27Instanciate a C<LATMOS::Accounts::Bases::Sql::DataRequest> object over an
28existing form definition (C<$accreq>).
29
30C<$id> is an optional DataRequest id to fetch an aready stored set of data.
31
32=cut
33
34sub new {
35    my ($class, $accreq, $id) = @_;
36
37    bless {
38        id => $id,
39        accreq => $accreq,
40    }, $class
41}
42
43=head2 ar_id
44
45Return the id of this form
46
47=cut
48
49sub ar_id { $_[0]->{id} }
50
51=head2 otype
52
53=cut
54
55sub otype { $_[0]->accreq->get_attributes('oType') }
56
57=head2 attributes ($attr)
58
59Return the value for attributes C<$attr> of attached C<Accreq>.
60
61Return attr
62
63=cut
64
65sub attributes {
66    $_[0]->accreq->get_attributes('attributes')
67}
68
69=head2 attribute_info ($attr)
70
71Return information for attribute C<$attr> of attached C<Accreq>.
72
73=cut
74
75sub attribute_info {
76    my ($self, $attr) = @_;
77    $self->accreq->attr_info($attr);
78}
79
80=head2 oobject
81
82Return object attached to this form if any
83
84=cut
85
86sub oobject {
87    my ($self) = @_;
88    if (my $name = $self->object_name) {
89        return $self->base->get_object($self->otype, $name);
90    } else {
91        return undef;
92    }
93}
94
95=head2 set_ptr_object ($ptr)
96
97Set the name of attached object, C<$ptr> can either the object name, either the
98object itself.
99
100=cut
101
102sub set_ptr_object {
103    my ($self, $ptr) = @_;
104
105    if (ref $ptr) {
106        $self->{_object_name} = $ptr->id;
107    } else {
108        $self->{_object_name} = $ptr;
109    }
110}
111
112=head2 is_for_new_object
113
114Return true if the form is about object creation
115
116=cut
117
118sub is_for_new_object {
119    my ($self) = @_;
120
121    $self->accreq->get_attributes('requireObject') ? 0 : 1;
122}
123
124# Fetch informations and cache it
125sub _infos {
126    my ($self) = @_;
127
128    return $self->{_infos} if ($self->{_infos});
129
130    my $sth = $self->accreq->db->prepare(q{
131        select *, now() >= apply as due
132        from request where id = ?
133    });
134
135    $sth->execute($self->ar_id);
136
137    return $self->{_infos} = $sth->fetchrow_hashref;
138}
139
140=head2 object_name
141
142Return the name of attached object
143
144=cut
145
146sub object_name {
147    my ($self) = @_;
148
149    return $self->{_object_name} if ($self->{_object_name});
150
151    return ($self->_infos || {})->{object};
152}
153
154=head2 user
155
156Return the user id submiting the request if any
157
158=cut
159
160sub user {
161    my ($self) = @_;
162
163    return ($self->_infos || {})->{user};
164}
165
166=head2 o_user
167
168Return the user object, see L</user>
169
170=cut
171
172sub o_user {
173    my ($self) = @_;
174
175    my $user = ($self->_infos || {})->{user};
176    return $self->base->get_object('user', $user);
177}
178
179=head2 apply
180
181Return the date the form must be validated
182
183=cut
184
185sub apply {
186    my ($self) = @_;
187
188    return ($self->_infos || {})->{apply};
189}
190
191=head2 due
192
193=cut
194
195sub due {
196    my ($self) = @_;
197
198    return ($self->_infos || {})->{due};
199}
200
201=head2 automated
202
203Return true if form is an automated one
204
205=cut
206
207sub automated {
208    my ($self) = @_;
209
210    return ($self->_infos || {})->{automated};
211}
212
213=head2 objrev
214
215Return the object revision at form registration
216
217=cut
218
219sub objrev {
220    my ($self) = @_;
221
222    return ($self->_infos || {})->{objrev};
223}
224
225=head2 register ($config, %info)
226
227Register the form. C<%info> must contains submitted informations.
228
229C<$config> is a hashref where
230
231=over 4
232
233=item user
234
235The username registering this form
236
237=item auto
238
239The form must automatically be validated
240
241=item apply
242
243The date the form must be validated
244
245=back
246
247=cut
248
249sub register {
250    my ($self, $config, %param) = @_;
251
252    my $user = $config->{user};
253    my $apply = $config->{apply};
254    my $auto = $config->{auto};
255
256    my $rev;
257
258    if (!$self->is_for_new_object()) {
259        if ($self->oobject) {
260            $rev = $self->oobject->get_attributes('rev');
261        } else {
262            la_log(LA_ERR, "An object type %s is required", $self->otype);
263            return;
264        }
265    }
266
267    my $sth_req = $self->base->db->prepare(q{
268        INSERT into request (name, object, "user", apply, automated, objrev)
269        values (?,?,?,?,?,?)
270        });
271    $apply ||= scalar(localtime);
272    $sth_req->execute($self->accreq->id, $self->object_name, $user, $apply,
273        $auto ? 1 : 0, $rev) or return;
274
275    my $newid = $self->base->db->last_insert_id(
276        undef,undef,undef,undef,
277        { sequence => 'request_id_seq'}
278    ) or do {
279        la_log(LA_ERR, "Cannot get new request id");
280        $self->base->db->rollback;
281        return;
282    };
283
284    my $sth_attr = $self->base->db->prepare(q{
285        insert into request_attributes (reqid, attribute, value)
286        values (?,?,?)
287    });
288
289    my $attr_count = 0;
290    foreach ($self->attributes) {
291        if (exists($param{$_})) {
292            if ($self->oobject) {
293                if (join('|', $self->oobject->get_attributes($_) || '')
294                    eq
295                    join('|', ref $param{$_} ? @{ $param{$_} } : $param{$_})) {
296                    # No diff
297                    next;
298                }
299            }
300            if (!$param{$_}) {
301                $attr_count += $sth_attr->execute($newid, $_, undef)
302                    if ($self->oobject);
303            } elsif(ref $param{$_}) {
304                foreach my $v (@{$param{$_}}) {
305                    $attr_count += $sth_attr->execute($newid, $_, $v);
306                }
307            } else {
308                $attr_count += $sth_attr->execute($newid, $_, $param{$_});
309            }
310        }
311    }
312
313    if ($attr_count) {
314        $self->{id} = $newid;
315        $self->base->db->commit;
316        $self->notify;
317        # flush cache
318        $self->{_infos} = undef;
319        return 1;
320    } else {
321        la_log(LA_ERR, "Nothing to store into the request");
322        $self->base->db->rollback;
323        return;
324    }
325}
326
327=head2 notify
328
329Send mail when registering
330
331=cut
332
333sub notify {
334    my ($self) = @_;
335
336    if (!$self->accreq->get_attributes('notifyMail')) {
337        return;
338    }
339
340    require LATMOS::Accounts::Mail;
341
342    my $submiter = $self->user
343        ? $self->base->get_object('user', $self->user)
344        : undef;
345
346    my %mail = (
347        to => $self->accreq->get_attributes('notifyMail'),
348        cc => ($submiter
349            ? $submiter->get_attributes('mail')
350            : ''),
351        Subject => sprintf('New request: %s',
352            $self->accreq->get_attributes('description')),
353        'X-LATMOS-Reason' => 'User request',
354    );
355
356    my $text = '';
357
358    if ($submiter) {
359        $text.= "\n";
360        $text .= "Request from " . $submiter->get_attributes('displayName');
361        $text .= "\n";
362    }
363
364    if (my $obj = $self->oobject) {
365        $text .= "For object " . $self->otype . ' ';
366        $text .= $obj->id;
367        if (my $dpn = $obj->get_attributes('displayName')) {
368            $text .= " ($dpn)"; 
369        }
370        $text .= "\n\n";
371    }
372
373    my %vals = $self->get_values;
374    foreach my $attr ($self->attributes) {
375        if (exists($vals{$attr})) {
376            foreach (ref $vals{$attr} ? @{ $vals{$attr} } : $vals{$attr}) {
377                $text .= "$attr: $_\n";
378            }
379        }
380    }
381
382    $text .= "\n\n";
383    $text .= "To apply: " . $self->apply;
384
385    my $lamail = LATMOS::Accounts::Mail->new(
386        $self->base->la,
387        \$text,
388    );
389
390
391    if ($lamail->process(
392        \%mail,
393        )) {
394        la_log(LA_NOTICE, "Request mail sent to %s", $mail{to});
395    }
396}
397
398=head2 get_values
399
400Return hash with registering value
401
402=cut
403
404sub get_values {
405    my ($self) = @_;
406
407    my $sth_f = $self->base->db->prepare(q{
408        select attribute, value from request_attributes where reqid = ?
409            order by attribute
410    });
411
412    my %values;
413    $sth_f->execute($self->ar_id);
414    while (my $res = $sth_f->fetchrow_hashref) {
415        my $attr = $res->{attribute};
416        if (exists($values{$attr})) {
417            if (!ref($values{$attr})) {
418                if (my $v = $values{$attr}) {
419                    $values{$attr} = [ grep { $_ } ($v, $res->{value}) ];
420                } else {
421                    $values{$attr} = $res->{value};
422                }
423            } elsif ($res->{value}) {
424                push(@{$values{$attr}}, $res->{value});
425            }
426        } else {
427            $values{$attr} = $res->{value};
428        }
429    }
430
431    %values
432}
433
434sub _register_applied {
435    my ($self, $comment) = @_;
436
437    my $sth = $self->base->db->prepare(q{
438        update request set done=now(), applied = true,
439        reason = ? where id = ?
440    });
441    $sth->execute($comment, $self->ar_id);
442}
443
444=head2 register_discard ($comment)
445
446Discard the form
447
448=cut
449
450sub register_discard {
451    my ($self, $comment) = @_;
452
453    my $sth = $self->base->db->prepare(q{
454        update request set done=now(), applied = false,
455        reason = ? where id = ?
456    });
457    $sth->execute($comment, $self->ar_id);
458}
459
460sub _prepare_attrs {
461    my ($self, %attrs) = @_;
462
463    my %newvalues = $self->get_values;
464    foreach (keys %attrs) {
465        $newvalues{$_} = $attrs{$_};
466    }
467    foreach (keys %newvalues) {
468        if (!$self->base->attribute($self->otype, $_)) {
469            delete($newvalues{$_});
470        }
471    }
472
473    return %newvalues;
474}
475
476=head2 unset_auto
477
478Set the form has no longer automatic
479
480=cut
481
482sub unset_auto {
483    my ($self) = @_;
484
485    my $sth = $self->accreq->db->prepare(q{
486        update request  set automated = false where id = ?
487    });
488    return $sth->execute($self->ar_id);
489}
490
491=head2 auto_apply_to_object ($comment)
492
493Apply modification automatically
494
495=cut
496
497sub auto_apply_to_object {
498    my ($self, $comment) = @_;
499
500    if (!$self->is_for_new_object) {
501        if (my $obj = $self->oobject) {
502            if ($obj->get_attributes('rev') != $self->objrev) {
503                $self->base->log(LA_WARN,
504                    'Object %s has been modified, set request as non
505                    automated');
506
507                $self->unset_auto;
508                return 1; # The automatic action "succeed"
509            }
510        } else {
511            $self->base->log('Cannot apply to non existing object %s',
512                $self->object_name);
513            return;
514        }
515    }
516    $self->apply_to_object($comment);
517}
518
519=head2 apply_to_object ($comment, %attrs)
520
521Apply modification to the object, C<%attrs> overload informations from forms.
522
523=cut
524
525sub apply_to_object {
526    my ($self, $comment, %attrs) = @_;
527
528    $self->check_acl or do {
529        $self->base->log(LA_ERR, 'Can\'t apply to object, permission denied by acl');
530    };
531
532    my %newvalues = $self->_prepare_attrs(%attrs);
533
534    if ($self->is_for_new_object) {
535        if ($self->base->create_object(
536            $self->otype,
537            $attrs{_name},
538            %attrs
539        )) {
540            $self->_register_applied($comment);
541            return 1;
542        } else {
543            $self->base->log(
544                LA_ERR,
545                'Error applying DataRequest id=%d to create object %s/%s: %s',
546                $self->ar_id,
547                $self->otype,
548                $attrs{_name},
549                $self->base->db->errstr,
550            );
551            return;
552        }
553    } else {
554        my $obj = $self->oobject or return;
555
556        if (defined($obj->set_c_fields(%newvalues)) &&
557            $self->_register_applied($comment)) {
558            $self->base->log(
559                LA_INFO,
560                'DataRequest id=%d applied to object %s/%s',
561                $self->ar_id,
562                $self->otype,
563                $obj->id,
564            );
565            return 1;
566        } else {
567            $self->base->log(
568                LA_ERR,
569                'Error applying DataRequest id=%d to object %s/%s: %s',
570                $self->ar_id,
571                $self->otype,
572                $obj->id,
573                $self->base->db->errstr,
574            );
575            $self->base->rollback;
576            return;
577        }
578    }
579}
580
581=head2 FUNCTION TO HANDLE OBJECT
582
583The following functions are provided to use a
584C<Datarequest> with the same way a C<LATMOS::Accounts::Bases::Object>.
585
586See L<LATMOS::Accounts::Bases::Object> documentation.
587
588=cut
589
590=head2 id
591
592=cut
593
594sub id { $_[0]->object_name }
595
596=head2 base
597
598=cut
599
600sub base { $_[0]->accreq->base }
601
602=head2 accreq
603
604=cut
605
606sub accreq { $_[0]->{accreq} }
607
608=head2 type
609
610=cut
611
612sub type { $_[0]->otype }
613
614=head2 get_attributes
615
616=cut
617
618sub get_attributes {
619    my ($self, @args) = @_;
620    if ($self->ar_id) {
621        # TODO not fetching all values each time
622        my %values = $self->get_values;
623        return $values{$args[0]};
624    } elsif (my $obj = $self->oobject) {
625        return $obj->get_attributes(@args);
626    } else {
627        return;
628    }
629}
630
631=head2 attribute
632
633=cut
634
635sub attribute {
636    my ($self, $attrname) = @_;
637
638    my $attr = $self->_attribute($attrname);
639
640    $attr->{_noacl} = 1;
641
642    $attr
643}
644
645sub _attribute {
646    my ($self, $attrname) = @_;
647
648    if (my $info = $self->attribute_info($attrname)) {
649        return $self->base->attribute($self->otype, $info);
650    } else {
651        my $obj = $self->oobject;
652        my $oo =  $obj
653            ? $obj->attribute($attrname)
654            : $self->base->attribute($self->otype, $attrname);
655        return $oo || $self->base->attribute($self->otype, { name => $attrname });
656    }
657}
658
659=head2 check_acl
660
661Return true if current connected user can validate the request
662
663=cut
664
665sub check_acl {
666    my ($self) = @_;
667
668    my $attr = $self->accreq->parse_form();
669    if (exists($attr->{validators})) {
670        my $acl = LATMOS::Accounts::Acls::Acl->new(
671            '*.*',
672            [ map { "  $_: write"} @{$attr->{validators} || [] } ]
673        );
674        my ($who, $groups) = ($self->base->user || '');
675        if ($who && (my $uo = $self->base->get_object('user', $who))) {
676            $groups = [ $uo->_get_attributes('memberOf') ];
677        } else {
678            $who = '';
679        } 
680
681        my $res = $acl->match($self->oobject || $self->otype, 'valid', 'w', $self->base->user, $groups);
682        defined($res) and return $res;
683
684        return;
685    }
686
687    # Check global Acl
688    if ($self->base->check_acl('request', 'VALIDATE', 'w')) {
689        return 1;
690    }
691
692    my $res = $self->_check_attr_acl;
693    return $res
694}
695
696sub _check_attr_acl {
697    my ($self) = @_;
698
699    if ($self->is_for_new_object) {
700        return $self->base->check_acl($self->otype, 'CREATE', 'w');
701    } else {
702        my $obj = $self->oobject;
703        foreach my $attr ($self->attributes) {
704            $self->base->check_acl($obj, $attr, 'w') or return;
705        }
706        return 1;
707    }
708}
709
710=head2 check_is_owner
711
712Return true if the connected user is the original requester
713
714=cut
715
716sub check_is_owner {
717    my ($self) = @_;
718
719    return (($self->base->user || '')  eq ($self->user || '--'))
720}
721
7221;
723
724__END__
725
726=head1 SEE ALSO
727
728L<LATMOS::Accounts::Bases::Sql>, L<LATMOS::Accounts::Bases::Sql::Accreq>
729
730=head1 AUTHOR
731
732Olivier Thauvin, E<lt>olivier.thauvin@latmos.ipsl.frE<gt>
733
734=head1 COPYRIGHT AND LICENSE
735
736Copyright (C) 2008, 2009 CNRS SA/CETP/LATMOS
737
738This library is free software; you can redistribute it and/or modify
739it under the same terms as Perl itself, either Perl version 5.10.0 or,
740at your option, any later version of Perl 5 you may have available.
741
742=cut
Note: See TracBrowser for help on using the repository browser.