]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Actor/Container.pm
Whitespace. gah.
[working/Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Actor / Container.pm
1 package OpenILS::Application::Actor::Container;
2 use base 'OpenILS::Application';
3 use strict; use warnings;
4 use OpenILS::Application::AppUtils;
5 use OpenILS::Perm;
6 use Data::Dumper;
7 use OpenSRF::EX qw(:try);
8 use OpenILS::Utils::Fieldmapper;
9 use OpenILS::Utils::CStoreEditor qw/:funcs/;
10 use OpenSRF::Utils::SettingsClient;
11 use OpenSRF::Utils::Cache;
12 use Digest::MD5 qw(md5_hex);
13 use OpenSRF::Utils::JSON;
14
15 my $apputils = "OpenILS::Application::AppUtils";
16 my $U = $apputils;
17 my $logger = "OpenSRF::Utils::Logger";
18
19 sub initialize { return 1; }
20
21 my $svc = 'open-ils.cstore';
22 my $meth = 'open-ils.cstore.direct.container';
23 my %types;
24 my %ctypes;
25 $types{'biblio'} = "$meth.biblio_record_entry_bucket";
26 $types{'callnumber'} = "$meth.call_number_bucket";
27 $types{'copy'} = "$meth.copy_bucket";
28 $types{'user'} = "$meth.user_bucket";
29 $ctypes{'biblio'} = "container_biblio_record_entry_bucket";
30 $ctypes{'callnumber'} = "container_call_number_bucket";
31 $ctypes{'copy'} = "container_copy_bucket";
32 $ctypes{'user'} = "container_user_bucket";
33 my $event;
34
35 sub _sort_buckets {
36         my $buckets = shift;
37         return $buckets unless ($buckets && $buckets->[0]);
38         return [ sort { $a->name cmp $b->name } @$buckets ];
39 }
40
41 __PACKAGE__->register_method(
42         method  => "bucket_retrieve_all",
43         api_name        => "open-ils.actor.container.all.retrieve_by_user",
44     authoritative => 1,
45         notes           => <<"  NOTES");
46                 Retrieves all un-fleshed buckets assigned to given user 
47                 PARAMS(authtoken, bucketOwnerId)
48                 If requestor ID is different than bucketOwnerId, requestor must have
49                 VIEW_CONTAINER permissions.
50         NOTES
51
52 sub bucket_retrieve_all {
53         my($self, $client, $auth, $user_id) = @_;
54     my $e = new_editor(authtoken => $auth);
55     return $e->event unless $e->checkauth;
56
57     if($e->requestor->id ne $user_id) {
58         return $e->event unless $e->allowed('VIEW_CONTAINER');
59     }
60     
61         my %buckets;
62     for my $type (keys %ctypes) {
63         my $meth = "search_" . $ctypes{$type};
64             $buckets{$type} = $e->$meth({owner => $user_id});
65     }
66
67         return \%buckets;
68 }
69
70 __PACKAGE__->register_method(
71         method  => "bucket_flesh",
72         api_name        => "open-ils.actor.container.flesh",
73     authoritative => 1,
74         argc            => 3, 
75 );
76
77 __PACKAGE__->register_method(
78         method  => "bucket_flesh_pub",
79         api_name        => "open-ils.actor.container.public.flesh",
80         argc            => 3, 
81 );
82
83 sub bucket_flesh {
84         my($self, $conn, $auth, $class, $bucket_id) = @_;
85     my $e = new_editor(authtoken => $auth);
86     return $e->event unless $e->checkauth;
87     return _bucket_flesh($self, $conn, $e, $class, $bucket_id);
88 }
89
90 sub bucket_flesh_pub {
91     my($self, $conn, $class, $bucket_id) = @_;
92     my $e = new_editor();
93     return _bucket_flesh($self, $conn, $e, $class, $bucket_id);
94 }
95
96 sub _bucket_flesh {
97         my($self, $conn, $e, $class, $bucket_id) = @_;
98         my $meth = 'retrieve_' . $ctypes{$class};
99     my $bkt = $e->$meth($bucket_id) or return $e->event;
100
101         unless($U->is_true($bkt->pub)) {
102         return undef if $self->api_name =~ /public/;
103         unless($bkt->owner eq $e->requestor->id) {
104             my $owner = $e->retrieve_actor_user($bkt->owner)
105                 or return $e->die_event;
106             return $e->event unless $e->allowed('VIEW_CONTAINER', $owner->home_ou);
107         }
108         }
109
110     my $fmclass = $bkt->class_name . "i";
111     $meth = 'search_' . $ctypes{$class} . '_item';
112         $bkt->items(
113         $e->$meth(
114             {bucket => $bucket_id}, 
115             {   order_by => {$fmclass => "pos"},
116                 flesh => 1, 
117                 flesh_fields => {$fmclass => ['notes']}
118             }
119         )
120     );
121
122         return $bkt;
123 }
124
125
126 __PACKAGE__->register_method(
127         method  => "item_note_cud",
128         api_name        => "open-ils.actor.container.item_note.cud",
129 );
130
131
132 sub item_note_cud {
133     my($self, $conn, $auth, $class, $note) = @_;
134     my $e = new_editor(authtoken => $auth, xact => 1);
135     return $e->die_event unless $e->checkauth;
136
137     my $meth = 'retrieve_' . $ctypes{$class};
138     my $nclass = $note->class_name;
139     (my $iclass = $nclass) =~ s/n$//og;
140
141     my $db_note = $e->$meth($note->id, {
142         flesh => 2,
143         flesh_fields => {
144             $nclass => ['item'],
145             $iclass => ['bucket']
146         }
147     });
148
149     if($db_note->item->bucket->owner ne $e->requestor->id) {
150         return $e->die_event unless 
151             $e->allowed('UPDATE_CONTAINER', $db_note->item->bucket);
152     }
153
154     $meth = 'create_' . $ctypes{$class} if $note->isnew;
155     $meth = 'update_' . $ctypes{$class} if $note->ischanged;
156     $meth = 'delete_' . $ctypes{$class} if $note->isdeleted;
157     return $e->die_event unless $e->$meth($note);
158     $e->commit;
159 }
160
161
162 __PACKAGE__->register_method(
163         method  => "bucket_retrieve_class",
164         api_name        => "open-ils.actor.container.retrieve_by_class",
165         argc            => 3, 
166         notes           => <<"  NOTES");
167                 Retrieves all un-fleshed buckets by class assigned to given user 
168                 PARAMS(authtoken, bucketOwnerId, class [, type])
169                 class can be one of "biblio", "callnumber", "copy", "user"
170                 The optional "type" parameter allows you to limit the search by 
171                 bucket type.  
172                 If bucketOwnerId is not defined, the authtoken is used as the
173                 bucket owner.
174                 If requestor ID is different than bucketOwnerId, requestor must have
175                 VIEW_CONTAINER permissions.
176         NOTES
177
178 sub bucket_retrieve_class {
179         my( $self, $client, $authtoken, $userid, $class, $type ) = @_;
180
181         my( $staff, $user, $evt ) = 
182                 $apputils->checkses_requestor( $authtoken, $userid, 'VIEW_CONTAINER' );
183         return $evt if $evt;
184
185         $logger->debug("User " . $staff->id . 
186                 " retrieving buckets for user $userid [class=$class, type=$type]");
187
188         my $meth = $types{$class} . ".search.atomic";
189         my $buckets;
190
191         if( $type ) {
192                 $buckets = $apputils->simplereq( $svc, 
193                         $meth, { owner => $userid, btype => $type } );
194         } else {
195                 $logger->debug("Grabbing buckets by class $class: $svc : $meth :  {owner => $userid}");
196                 $buckets = $apputils->simplereq( $svc, $meth, { owner => $userid } );
197         }
198
199         return _sort_buckets($buckets);
200 }
201
202 __PACKAGE__->register_method(
203         method  => "bucket_create",
204         api_name        => "open-ils.actor.container.create",
205         notes           => <<"  NOTES");
206                 Creates a new bucket object.  If requestor is different from
207                 bucketOwner, requestor needs CREATE_CONTAINER permissions
208                 PARAMS(authtoken, bucketObject);
209                 Returns the new bucket object
210         NOTES
211
212 sub bucket_create {
213         my( $self, $client, $authtoken, $class, $bucket ) = @_;
214
215         my $e = new_editor(xact=>1, authtoken=>$authtoken);
216         return $e->event unless $e->checkauth;
217
218         if( $bucket->owner ne $e->requestor->id ) {
219                 return $e->event unless
220                         $e->allowed('CREATE_CONTAINER');
221
222         } else {
223                 return $e->event unless
224                         $e->allowed('CREATE_MY_CONTAINER');
225         }
226                 
227         $bucket->clear_id;
228
229     my $evt = OpenILS::Event->new('CONTAINER_EXISTS', 
230         payload => [$class, $bucket->owner, $bucket->btype, $bucket->name]);
231     my $search = {name => $bucket->name, owner => $bucket->owner, btype => $bucket->btype};
232
233         my $obj;
234         if( $class eq 'copy' ) {
235         return $evt if $e->search_container_copy_bucket($search)->[0];
236                 return $e->event unless
237                         $obj = $e->create_container_copy_bucket($bucket);
238         }
239
240         if( $class eq 'callnumber' ) {
241         return $evt if $e->search_container_call_number_bucket($search)->[0];
242                 return $e->event unless
243                         $obj = $e->create_container_call_number_bucket($bucket);
244         }
245
246         if( $class eq 'biblio' ) {
247         return $evt if $e->search_container_biblio_record_entry_bucket($search)->[0];
248                 return $e->event unless
249                         $obj = $e->create_container_biblio_record_entry_bucket($bucket);
250         }
251
252         if( $class eq 'user') {
253         return $evt if $e->search_container_user_bucket($search)->[0];
254                 return $e->event unless
255                         $obj = $e->create_container_user_bucket($bucket);
256         }
257
258         $e->commit;
259         return $obj->id;
260 }
261
262
263 __PACKAGE__->register_method(
264         method  => "item_create",
265         api_name        => "open-ils.actor.container.item.create",
266     signature => {
267         desc => q/
268             Adds one or more items to an existing container
269         /,
270         params => [
271                     {desc => 'Authentication token', type => 'string'},
272                     {desc => 'Container class.  Can be "copy", "callnumber", "biblio", or "user"', type => 'string'},
273                     {desc => 'Item or items.  Can either be a single container item object, or an array of them', type => 'object'},
274         ],
275         return => {
276             desc => 'The ID of the newly created item(s).  In batch context, an array of IDs is returned'
277         }
278     }
279 );
280
281
282 sub item_create {
283         my( $self, $client, $authtoken, $class, $item ) = @_;
284
285         my $e = new_editor(xact=>1, authtoken=>$authtoken);
286         return $e->die_event unless $e->checkauth;
287     my $items = (ref $item eq 'ARRAY') ? $item : [$item];
288
289         my ( $bucket, $evt ) = $apputils->fetch_container_e($e, $item->bucket, $class);
290         return $evt if $evt;
291
292         if( $bucket->owner ne $e->requestor->id ) {
293                 return $e->die_event unless
294                         $e->allowed('CREATE_CONTAINER_ITEM');
295
296         } else {
297 #               return $e->event unless
298 #                       $e->allowed('CREATE_CONTAINER_ITEM'); # new perm here?
299         }
300                 
301     for my $one_item (@$items) {
302
303         $one_item->clear_id;
304
305         my $stat;
306         if( $class eq 'copy' ) {
307             return $e->die_event unless
308                 $stat = $e->create_container_copy_bucket_item($one_item);
309         }
310
311         if( $class eq 'callnumber' ) {
312             return $e->die_event unless
313                 $stat = $e->create_container_call_number_bucket_item($one_item);
314         }
315
316         if( $class eq 'biblio' ) {
317             return $e->die_event unless
318                 $stat = $e->create_container_biblio_record_entry_bucket_item($one_item);
319         }
320
321         if( $class eq 'user') {
322             return $e->die_event unless
323                 $stat = $e->create_container_user_bucket_item($one_item);
324         }
325     }
326
327         $e->commit;
328
329     # CStoreEeditor inserts the id (pkey) on newly created objects
330     return [ map { $_->id } @$items ] if ref $item eq 'ARRAY';
331         return $item->id; 
332 }
333
334
335
336 __PACKAGE__->register_method(
337         method  => "item_delete",
338         api_name        => "open-ils.actor.container.item.delete",
339         notes           => <<"  NOTES");
340                 PARAMS(authtoken, class, itemId)
341         NOTES
342
343 sub item_delete {
344         my( $self, $client, $authtoken, $class, $itemid ) = @_;
345
346         my $e = new_editor(xact=>1, authtoken=>$authtoken);
347         return $e->event unless $e->checkauth;
348
349         my $ret = __item_delete($e, $class, $itemid);
350         $e->commit unless $U->event_code($ret);
351         return $ret;
352 }
353
354 sub __item_delete {
355         my( $e, $class, $itemid ) = @_;
356         my( $bucket, $item, $evt);
357
358         ( $item, $evt ) = $U->fetch_container_item_e( $e, $itemid, $class );
359         return $evt if $evt;
360
361         ( $bucket, $evt ) = $U->fetch_container_e($e, $item->bucket, $class);
362         return $evt if $evt;
363
364         if( $bucket->owner ne $e->requestor->id ) {
365       my $owner = $e->retrieve_actor_user($bucket->owner)
366          or return $e->die_event;
367                 return $e->event unless $e->allowed('DELETE_CONTAINER_ITEM', $owner->home_ou);
368         }
369
370         my $stat;
371         if( $class eq 'copy' ) {
372         for my $note (@{$e->search_container_copy_bucket_item_note({item => $item->id})}) {
373             return $e->event unless 
374                 $e->delete_container_copy_bucket_item_note($note);
375         }
376                 return $e->event unless
377                         $stat = $e->delete_container_copy_bucket_item($item);
378         }
379
380         if( $class eq 'callnumber' ) {
381         for my $note (@{$e->search_container_call_number_bucket_item_note({item => $item->id})}) {
382             return $e->event unless 
383                 $e->delete_container_call_number_bucket_item_note($note);
384         }
385                 return $e->event unless
386                         $stat = $e->delete_container_call_number_bucket_item($item);
387         }
388
389         if( $class eq 'biblio' ) {
390         for my $note (@{$e->search_container_biblio_record_entry_bucket_item_note({item => $item->id})}) {
391             return $e->event unless 
392                 $e->delete_container_biblio_record_entry_bucket_item_note($note);
393         }
394                 return $e->event unless
395                         $stat = $e->delete_container_biblio_record_entry_bucket_item($item);
396         }
397
398         if( $class eq 'user') {
399         for my $note (@{$e->search_container_user_bucket_item_note({item => $item->id})}) {
400             return $e->event unless 
401                 $e->delete_container_user_bucket_item_note($note);
402         }
403                 return $e->event unless
404                         $stat = $e->delete_container_user_bucket_item($item);
405         }
406
407         return $stat;
408 }
409
410
411 __PACKAGE__->register_method(
412         method  => 'full_delete',
413         api_name        => 'open-ils.actor.container.full_delete',
414         notes           => "Complety removes a container including all attached items",
415 );      
416
417 sub full_delete {
418         my( $self, $client, $authtoken, $class, $containerId ) = @_;
419         my( $container, $evt);
420
421         my $e = new_editor(xact=>1, authtoken=>$authtoken);
422         return $e->event unless $e->checkauth;
423
424         ( $container, $evt ) = $apputils->fetch_container_e($e, $containerId, $class);
425         return $evt if $evt;
426
427         if( $container->owner ne $e->requestor->id ) {
428       my $owner = $e->retrieve_actor_user($container->owner)
429          or return $e->die_event;
430                 return $e->event unless $e->allowed('DELETE_CONTAINER', $owner->home_ou);
431         }
432
433         my $items; 
434
435         my @s = ({bucket => $containerId}, {idlist=>1});
436
437         if( $class eq 'copy' ) {
438                 $items = $e->search_container_copy_bucket_item(@s);
439         }
440
441         if( $class eq 'callnumber' ) {
442                 $items = $e->search_container_call_number_bucket_item(@s);
443         }
444
445         if( $class eq 'biblio' ) {
446                 $items = $e->search_container_biblio_record_entry_bucket_item(@s);
447         }
448
449         if( $class eq 'user') {
450                 $items = $e->search_container_user_bucket_item(@s);
451         }
452
453         __item_delete($e, $class, $_) for @$items;
454
455         my $stat;
456         if( $class eq 'copy' ) {
457                 return $e->event unless
458                         $stat = $e->delete_container_copy_bucket($container);
459         }
460
461         if( $class eq 'callnumber' ) {
462                 return $e->event unless
463                         $stat = $e->delete_container_call_number_bucket($container);
464         }
465
466         if( $class eq 'biblio' ) {
467                 return $e->event unless
468                         $stat = $e->delete_container_biblio_record_entry_bucket($container);
469         }
470
471         if( $class eq 'user') {
472                 return $e->event unless
473                         $stat = $e->delete_container_user_bucket($container);
474         }
475
476         $e->commit;
477         return $stat;
478 }
479
480 __PACKAGE__->register_method(
481         method          => 'container_update',
482         api_name                => 'open-ils.actor.container.update',
483         signature       => q/
484                 Updates the given container item.
485                 @param authtoken The login session key
486                 @param class The container class
487                 @param container The container item
488                 @return true on success, 0 on no update, Event on error
489                 /
490 );
491
492 sub container_update {
493         my( $self, $conn, $authtoken, $class, $container )  = @_;
494
495         my $e = new_editor(xact=>1, authtoken=>$authtoken);
496         return $e->event unless $e->checkauth;
497
498         my ( $dbcontainer, $evt ) = $U->fetch_container_e($e, $container->id, $class);
499         return $evt if $evt;
500
501         if( $e->requestor->id ne $container->owner ) {
502                 return $e->event unless $e->allowed('UPDATE_CONTAINER');
503         }
504
505         my $stat;
506         if( $class eq 'copy' ) {
507                 return $e->event unless
508                         $stat = $e->update_container_copy_bucket($container);
509         }
510
511         if( $class eq 'callnumber' ) {
512                 return $e->event unless
513                         $stat = $e->update_container_call_number_bucket($container);
514         }
515
516         if( $class eq 'biblio' ) {
517                 return $e->event unless
518                         $stat = $e->update_container_biblio_record_entry_bucket($container);
519         }
520
521         if( $class eq 'user') {
522                 return $e->event unless
523                         $stat = $e->update_container_user_bucket($container);
524         }
525
526         $e->commit;
527         return $stat;
528 }
529
530
531
532 __PACKAGE__->register_method(
533         method  => "anon_cache",
534         api_name        => "open-ils.actor.anon_cache.set_value",
535     signature => {
536         desc => q/
537             Sets a value in the anon web cache.  If the session key is
538             undefined, one will be automatically generated.
539         /,
540         params => [
541                     {desc => 'Session key', type => 'string'},
542             {
543                 desc => q/Field name.  The name of the field in this cache session whose value to set/, 
544                 type => 'string'
545             },
546             {
547                 desc => q/The cached value.  This can be any type of object (hash, array, string, etc.)/,
548                 type => 'any'
549             },
550         ],
551         return => {
552             desc => 'session key on success, undef on error',
553             type => 'string'
554         }
555     }
556 );
557
558 __PACKAGE__->register_method(
559         method  => "anon_cache",
560         api_name        => "open-ils.actor.anon_cache.get_value",
561     signature => {
562         desc => q/
563             Returns the cached data at the specified field within the specified cache session.
564         /,
565         params => [
566                     {desc => 'Session key', type => 'string'},
567             {
568                 desc => q/Field name.  The name of the field in this cache session whose value to set/, 
569                 type => 'string'
570             },
571         ],
572         return => {
573             desc => 'cached value on success, undef on error',
574             type => 'any'
575         }
576     }
577 );
578
579 __PACKAGE__->register_method(
580         method  => "anon_cache",
581         api_name        => "open-ils.actor.anon_cache.delete_session",
582     signature => {
583         desc => q/
584             Deletes a cache session.
585         /,
586         params => [
587                     {desc => 'Session key', type => 'string'},
588         ],
589         return => {
590             desc => 'Session key',
591             type => 'string'
592         }
593     }
594 );
595
596 sub anon_cache {
597     my($self, $conn, $ses_key, $field_key, $value) = @_;
598
599     my $sc = OpenSRF::Utils::SettingsClient->new;
600         my $cache = OpenSRF::Utils::Cache->new('anon');
601     my $cache_timeout = $sc->config_value(cache => anon => 'max_cache_time') || 1800; # 30 minutes
602     my $cache_size = $sc->config_value(cache => anon => 'max_cache_size') || 102400; # 100k
603
604     if($self->api_name =~ /delete_session/) {
605
606        return $cache->delete_cache($ses_key); 
607
608     }  elsif( $self->api_name =~ /set_value/ ) {
609
610         $ses_key = md5_hex(time . rand($$)) unless $ses_key;
611         my $blob = $cache->get_cache($ses_key) || {};
612         $blob->{$field_key} = $value;
613         return undef if 
614             length(OpenSRF::Utils::JSON->perl2JSON($blob)) > $cache_size; # bytes, characters, whatever ;)
615         $cache->put_cache($ses_key, $blob, $cache_timeout);
616         return $ses_key;
617
618     } else {
619
620         my $blob = $cache->get_cache($ses_key) or return undef;
621         return $blob if (!defined($field_key));
622         return $blob->{$field_key};
623     }
624 }
625
626
627
628 1;
629
630